2013-04-26 6 views
5

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

class Cmd: 
    cmd = None 

    @staticmethod 
    def cmdOne(): 
     print 'cmd one' 

    @staticmethod 
    def cmdTwo(): 
     print 'cmd two' 

def main(): 
    cmd = Cmd.cmdOne 
    cmd() # works fine 

    Cmd.cmd = Cmd.cmdOne   
    Cmd.cmd() # unbound error !! 

if __name__=="__main__": 
    main() 

Полную ошибка :

TypeError: unbound method cmdOne() must be called with Cmd instance as 
      first argument (got nothing instead) 

ответ

2

Мне нравится просматривать это поведение с «снизу вверх».

Функция в Python действует как «descriptor object». Таким образом, он имеет метод __get__().

Доступ к атрибуту класса, который имеет такой метод __get__(), «перенаправлен» на этот метод. Атрибут доступа к классу выполняется как attribute.__get__(None, containing_class), в то время как доступ к экземпляру атрибута сопоставляется с attribute.__get__(instance, containing_class).

Задача функции __get__() функции заключается в том, чтобы обернуть функцию в объект метода, который обертывает параметр self - для случая доступа атрибута к экземпляру. Это называется связанным методом.

В отношении атрибута класса на 2.x функция __get__() возвращает возвращенную оболочку метода, тогда как, как I learned today, на 3.x, она возвращается сама. (Обратите внимание, что механизм __get__() все еще существует в 3.x, но функция просто возвращает себя.) Это почти то же самое, если вы посмотрите, как он вызывается, но оболочка unbound-метода дополнительно проверяет правильный тип аргумента self.

A staticmethod() вызов просто создает объект, вызов которого __get__() предназначен для возврата первоначально заданного объекта, чтобы он не отклонил описанное поведение. Вот как работает HYRY's trick: атрибут acces отменяет обертку staticmethod(), вызов делает это снова, так что атрибут «новый» имеет тот же статус, что и старый, хотя в этом случае staticmethod(), кажется, применяется дважды (но на самом деле isn ' т).

(BTW: Это работает даже в этом странном контексте:.

s = staticmethod(8) 
t = s.__get__(None, 2) # gives 8 

хотя 8 не является функцией и 2 не класс)

В вашем вопросе, у вас есть две ситуации:

cmd = Cmd.cmdOne 
cmd() # works fine 

обращается к классу и просит его атрибута cmdOne, в staticmethod() объекта. Это запрашивается через его __get__() и возвращает исходную функцию, которая затем вызывается. Вот почему он отлично работает.

Cmd.cmd = Cmd.cmdOne 
Cmd.cmd() # unbound error 

делает то же самое, но затем присваивает эту функцию Cmd.cmd. Следующая строка - это доступ к атрибуту, который также вызывает вызов __get__() самой функции и, таким образом, возвращает несвязанный метод, который должен быть вызван с правильным объектом self в качестве первого аргумента.

3

Вы должны использовать staticmethod() для преобразования функции:

Cmd.cmd = staticmethod(Cmd.cmdOne) 
+0

Не отвечает на вопрос. – martineau

3

Y ou're работает в поведении «несвязанных методов» в Python 2.x. В принципе, в Python 2.x, когда вы получаете атрибут класса (например, в этом случае Cmd.cmd), а значение является функцией, то класс «обертывает» эту функцию в специальный объект «unbound method», потому что они предположим, что атрибуты классов, которые являются функциями и не украшены staticmethod или classmethod, предназначены как методы экземпляра (в этом случае неверное предположение). Этот несвязанный метод ожидает аргумент при вызове, хотя в этом случае базовая функция не ожидает аргумента.

Такое поведение объясняется в language reference: (раздел в "классов")

When a class attribute reference (for class C, say) would yield a user-defined function object or [...], it is transformed into an unbound user-defined method object whose im_class attribute is C.

(в разделе "определяемые пользователем методы")

When a user-defined method object is created by retrieving a user-defined function object from a class, its im_self attribute is None and the method object is said to be unbound.

[ ...]

When an unbound user-defined method object is called, the underlying function (im_func) is called, with the restriction that the first argument must be an instance of the proper class (im_class) or of a derived class thereof.

Это то, что вызывает ошибку, которую вы видите.

Вы можете явно извлечь основную функцию из объекта метода и называем это (но это, очевидно, не является идеальным нужно сделать это):

Cmd.cmd.im_func() 

Обратите внимание, что Python 3.x got rid of unbound methods и ваш код будет отлично работает на Python 3.x

+0

Точнее, это сама функция, которая «обертывает» себя в объект «unbound method»: путем доступа к нему класс вызывает функцию '__get __()', которая создает оболочку. – glglgl