Вопрос-Ответ

What is a "callable"?

Что такое "вызываемый"?

Теперь, когда ясно, что такое метакласс, есть связанное с ним понятие, которое я постоянно использую, не зная, что оно на самом деле означает.

Я полагаю, что все когда-то допускали ошибку со скобками, что приводило к исключению "объект не вызывается". Более того, использование __init__ и __new__ заставляет задуматься, для чего этот чертов __call__ можно использовать.

Не могли бы вы дать мне несколько объяснений, включая примеры с волшебным методом ?

Переведено автоматически
Ответ 1

Вызываемый объект - это все, что может быть вызвано.

Встроенный вызываемый (PyCallable_Check в objects.c) проверяет, является ли аргумент либо:


  • экземпляр класса с __call__ методом или

  • относится к типу, который имеет ненулевой член tp_call (c struct), который указывает на возможность вызова в противном случае (например, в функциях, методах и т.д.)

Метод с именем __call__ является (согласно документации)


Вызывается, когда экземпляр "вызывается" как функция


Пример

class Foo:
def __call__(self):
print 'called'

foo_instance = Foo()
foo_instance() #this is calling the __call__ method
Ответ 2

Из исходных текстов Python object.c:

/* Test whether an object can be called */

int
PyCallable_Check(PyObject *x)
{
if (x == NULL)
return 0;
if (PyInstance_Check(x)) {
PyObject *call = PyObject_GetAttrString(x, "__call__");
if (call == NULL) {
PyErr_Clear();
return 0;
}
/* Could test recursively but don't, for fear of endless
recursion if some joker sets self.__call__ = self */

Py_DECREF(call);
return 1;
}
else {
return x->ob_type->tp_call != NULL;
}
}

В нем говорится:


  1. Если объект является экземпляром некоторого класса, то он вызываем, если у него есть __call__ атрибут.

  2. Иначе объект x можно вызывать, если x->ob_type->tp_call != NULL

Описание tp_call поля:


ternaryfunc tp_call Необязательный указатель на функцию, которая реализует вызов объекта. Это значение должно быть NULL, если объект не вызывается. Сигнатура такая же, как для PyObject_Call() . Это поле наследуется подтипами.


Вы всегда можете использовать встроенную callable функцию, чтобы определить, является ли данный объект вызываемым или нет; или еще лучше просто вызвать его и перехватить TypeError позже. callable удалено в Python 3.0 и 3.1, используйте callable = lambda o: hasattr(o, '__call__') или isinstance(o, collections.Callable).

Пример упрощенной реализации кэша:

class Cached:
def __init__(self, function):
self.function = function
self.cache = {}

def __call__(self, *args):
try: return self.cache[args]
except KeyError:
ret = self.cache[args] = self.function(*args)
return ret

Использование:

@Cached
def ack(x, y):
return ack(x-1, ack(x, y-1)) if x*y else (x + y + 1)

Пример из стандартной библиотеки, файла site.py, определение встроенных exit() и quit() функций:

class Quitter(object):
def __init__(self, name):
self.name = name
def __repr__(self):
return 'Use %s() or %s to exit' % (self.name, eof)
def __call__(self, code=None):
# Shells like IDLE catch the SystemExit, but listen when their
# stdin wrapper is closed.
try:
sys.stdin.close()
except:
pass
raise SystemExit(code)
__builtin__.quit = Quitter('quit')
__builtin__.exit = Quitter('exit')
Ответ 3

Вызываемый объект позволяет использовать функцию круглых скобок ( ) и в конечном итоге передавать некоторые параметры, точно так же, как функции.

Каждый раз, когда вы определяете функцию, python создает вызываемый объект. В примере вы могли бы определить функцию func следующими способами (это одно и то же):

class a(object):
def __call__(self, *args):
print 'Hello'

func = a()

# or ...
def func(*args):
print 'Hello'

Вы могли бы использовать этот метод вместо таких методов, как doit или run, я думаю, что просто obj() виднее, чем obj.doit()

Ответ 4

Позвольте мне объяснить в обратном порядке:

Учтите это...

foo()

... в качестве синтаксического сахара для:

foo.__call__()

Где foo может быть любой объект, который реагирует на __call__. Когда я говорю "любой объект", я имею в виду это: встроенные типы, ваши собственные классы и их экземпляры.

В случае встроенных типов, когда вы пишете:

int('10')
unicode(10)

По сути, вы делаете:

int.__call__('10')
unicode.__call__(10)

Вот почему у вас также нет foo = new int в Python: вы просто заставляете объект класса возвращать его экземпляр на __call__. Способ, которым Python решает это, на мой взгляд, очень элегантный.

python