Python - функция как атрибут класса становится связанным методом

Я заметил, что если я определяю атрибут класса, равный функции, при создании экземпляра этого класса, атрибут становится связанным методом. Может кто-нибудь объяснить мне причину такого поведения?

In [9]: def func():
   ...:     pass
   ...: 

In [10]: class A(object):
   ....:     f = func
   ....:     

In [11]: a = A()

In [12]: a.f
Out[12]: <bound method A.func of <__main__.A object at 0x104add190>>

In [13]: a.f()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-13-19134f1ad9a8> in <module>()
----> 1 a.f()
    global a.f = <bound method A.func of <__main__.A object at 0x104add190>>

TypeError: func() takes no arguments (1 given)

3 ответа

Решение

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

Методы в классах получают хотя бы один параметр (self), если специально не сказано - см. методы класса и статические методы - по этой причине ошибка говорит, что func не принимает аргументов (как это определено как def func():) но получил 1 (self)

Чтобы сделать то, что вы хотите, вы должны сказать Python, что вы используете статический метод

def func():
    pass

class A(object):
    f = staticmethod(func)

Python не является ОО-системой, основанной на сообщениях1. Вместо этого, подобно JavaScript, свойства разрешаются в функции первого класса, а затем вызываются; Поведение немного отличается в механике таких, как обнаружено.

В Python требуется, чтобы у методов был хотя бы один параметр, обычно называемый self, это будет автоматически предоставлено ассоциированному экземпляру, когда он вызывается как метод.

Кроме того (и, возможно, что касается вопроса), Python не делает различий между использованием def f.. или же f = some_func() при установлении привязок членов экземпляра; возможно, это соответствует поведению вне классов.

В этом примере назначение функции экземпляру "заставляет ожидать, что она будет обрабатываться как метод экземпляра". Это одна и та же функция без параметров, вызываемая в обоих случаях; только будущее использование таких уместно.

Теперь, в отличие от JavaScript, Python обрабатывает методы и объектную ассоциацию через концепцию связанных методов - функции, разрешенные как методы, всегда "связаны".

Поведение a.f возврат метода привязки - функция, которая автоматически предоставит связанный объект первому параметру в виде self - выполняется независимо от источника функции. В этом случае это означает, что функция без параметров не может использоваться, когда она "связана", так как она не принимает self параметр.

В качестве демонстрации следующее не будет выполнено таким же образом, поскольку исходный метод не соответствует минимальным требованиям для принятия экземпляра в качестве аргумента:

g = a.f
g()

В этом случае зовет g() эквивалентно звонку func(a),


1 Для сравнения, Java, C#, Ruby и SmallTalk являются ОО-системами на основе сообщений - в них объекту предписывается вызывать метод по "имени" вместо разрешения метода (или функции) как значения, которое может быть вызвано,

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

      # Some arbitrary function
def f(x):
  return x

# A class, which will wrap the function as a dictionary object, which is a class attr
class Test:
  def __init__(self,f):
    self.f = {'f':f}
  
  def func(self,x):
    return self.f['f'](x)

# Now let's test the Test object
t = Test(f=f)
t.func(3)

>>>
3

Этот подход более подробен, чем принятый ответ, однако он не затрагивает статический метод, декораторы или другие сложные темы. Так что это сработает в крайнем случае, если вас пугает другой, предпочтительный подход.

Изменить: если вы хотите, чтобы это было достаточно надежным для обработки аргументов ключевого слова:

      class Test:
  def __init__(self,f):
    self.f = {'f':f}
  
  def func(self,p):
    return self.f['f'](**p)

def f(**p):
  return p['x'] * p['y']

t = Test(f=f)
t.func({'x':2,'y':3})

>>>
6
Другие вопросы по тегам