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:
1 2 3 4 5 6 7 8 9 |
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?
1 |
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:
1 2 3 4 5 6 7 8 9 |
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?
1 |
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:
1 2 |
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:
1 2 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# 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 |