Python: callable object

13-Sty-2018

Każdy obiekt ma swój typ i pewne właściwości. Jedną z tych właściwości jest informacja, czy obiekt można wywołać (dziwnie to brzmi po polsku „jest wywoływalny” więc będę używał „is callable”). Oczywiście funkcje są przykładem obiektów wywoływalnych. Zobacz:

def f_just_print_1():
 return 1
 
print('is function callable? ', callable(f_just_print_1))

x=123
 
print('is integer callable? ',callable(x))
Jak to uruchomisz to zobaczysz, że funkcja jest callable a liczba nie - logiczne.

Ale funkcja coś zwraca. W naszym przypadku zwraca liczbę. Czy liczba jest callable?

print('is result of function callable? ',callable(f_just_print_1()))

Jak uruchomisz ten przykład to zobaczysz, że nie. Funkcja się wywołała, zwróciła liczbę a liczba już nie jest callable. Nadal logiczne.

Ale – co by było, gdyby funkcja zwróciła obiekt funkcji? To bardzo dziwne, ale popatrz na taki prosty przykład:

def DoOperation(OperationType):
    def PLUS(a,b):
        return a+b
    def MINUS(a,b):
        return a-b
    if OperationType == 'ADD':
        return PLUS
    else:
        return MINUS

Mamy funkcję, która jako argument przyjmuje napis OperationType

  • jeśli OperationType to napis „ADD” to zostanie zwrócona funkcja PLUS, która z kolei przyjmuje dwa argumenty a i b. Funkcja PLUS dodaje te liczby
  • jeżeli OperationType to cokolwiek innego, to zostanie zwrócona funkcja MINUS, która z kolei przyjmuje dwa argumenty a i b. Funkcja MINUS odejmuje te liczby

Czy to co zwraca funkcja DoOperation jest callable?

print('is this callable? ',callable(DoOperation('ADD')))

Jeśli uruchomisz powyższą linijkę to zobaczysz, że tak. To skoro wynik zwracany przez funkcję można wywołać to co się stanie przy takim zapisie:

print('result of operation ADD: ',DoOperation('ADD')(3,1))
print('result of operation SUBSTRACT: ',DoOperation('SUBSTRACT')(3,1))
  • DoOperation(‚ADD’)(3,1) – najpierw wywołaliśmy DoOperation, która zwróciła funkcję PLUS, a potem funkcja PLUS uruchomiła się z argumentami 3 i 1 i wynik to 4
  • DoOperation(‚SUBSTRACT’)(3,1) – najpierw wywołaliśmy DoOperation, która zwróciła funkcję MINUS, a potem funkcja MINUS uruchomiła się z argumentami 3 i 1 i wynik to 2

Oczywiście można by to zrobić też tak:

myNewFunction = DoOperation('ADD')
print('result is ',myNewFunction(3,1))

ale ten zapis rozciąga się na 2 linijki, a notacja o którą pytasz jest bardziej kompaktowa.

O funkcji callable:

https://www.programiz.com/python-programming/methods/built-in/callable

Inny przykład bazujący na definicji klasy i wywołania metody klasy:

https://stackoverflow.com/questions/111234/what-is-a-callable-in-python

Różnie programiści korzystają z tej notacji. Znajdziesz ją między innymi w Keras:

https://keras.io/getting-started/functional-api-guide/

zwróć uwagę na komentarz:

# a layer instance is callable on a tensor, and returns a tensor
w tym fragmencie:

from keras.layers import Input, Dense
from keras.models import Model
# This returns a tensor
inputs = Input(shape=(784,))
# a layer instance is callable on a tensor, and returns a tensor
x = Dense(64, activation='relu')(inputs)
x = Dense(64, activation='relu')(x)
predictions = Dense(10, activation='softmax')(x)
# This creates a model that includes
# the Input layer and three Dense layers
model = Model(inputs=inputs, outputs=predictions)
model.compile(optimizer='rmsprop',
 loss='categorical_crossentropy',
 metrics=['accuracy'])
model.fit(data, labels) # starts training

Dodaj komentarz:

Autor: Rafał Kraik