2011-01-13 3 views
1

Допустим, у меня есть функция, как это:объектов класса скачков при назначении функции

 
def id(x): 
    return x 

я ожидаю, чтобы быть в состоянии связать его с любым объектом (скажем, х),

 
x.f = id 

и быть в состоянии сделать

 
x.f(1) 
=> 1 

Однако классы не являются постоянными объектами becau се это не работает:

 
class X(object): 
    pass 

X.f = id 
x.f(1) 
=> unbound method f() must be called with X instance as first argument (got int instance instead) 

Так что я должен сделать,

 
X.f=staticmethod(id) 
X.f(1) 
=> 1 

Однако он не работает с обычными объектами,

 
x.f=staticmethod(f) 
x.f(1) 
=> 'staticmethod' object is not callable 

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

+1

Вы не должны переопределять встроенный метод 'id()'. – robert

+1

«Я ожидаю, что смогу привязать его к любому объекту» Действительно? Зачем? Любая ссылка или цитата, которая задала бы это ожидание? –

ответ

3
import inspect 
if inspect.isclass(X): 
    X.f = staticmethod(f) 
else: 
    X.f = f 

Вопрос не в назначении; это прекрасно работает. Это то, что функции на классах принимают self как их первый параметр, а ваша функция - нет. Вы можете получить фактическую функцию обратно с

X.__dict__['f'] 
<function f at 0x023A52F0> 

или

X.f 
<unbound method X.f> 
X.f.im_func 
<function f at 0x023A52F0> 

(Вы получаете несвязанный метод вместо исходной функции, потому что в Python 2.x, что это то, что происходит, когда вы получаете доступ к Способ по классу. В Python 3.x, что изменилось, и вы просто получите оригинальную функцию. Я считаю, что это для обратной совместимости с классами старого стиля.)


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

+0

Действительно, с Python 3 функция не превращается в связанную функцию: «Xf = f» и «Xf (1)» => 1. – Eduardo

+0

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

1

Определите функцию, которую вы хотите связать, как это:

def id_(self, x): 
    return x 

и теперь вы можете сделать что-то вроде:

X.f = id_ 
x = X() 
x.f(1) 
=> 1 
+0

Дело не в этом. Дело в том, что это должно работать _generically_, без необходимости изменять функцию. Для этого и нужен вызов 'staticmethod'. – katrielalex

+0

@ katrielalex: Я думаю, что это полезно для OP, потому что из его вопроса я думаю, что он не понимал, почему его первый пример не работает, поэтому он думал, что он может использовать __staticmethod__, чтобы исправить это, а не просто изменить аргумент функции как показано выше в моем ответе (сопоставление обезьян) – mouad

+0

Если x является обычным объектом, это не сработает, но приведет к ошибке «f() принимает ровно 2 аргумента (1 задано)», поскольку f не привязывается при назначении регулярному объект. – Eduardo

1

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

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

class func2static(type): 
    def __setattr__(cls, name, value): 
     if type(value) is type(lambda: 0): 
      value = staticmethod(value) 
     type.__setattr__(cls, name, value) 

class X(object): 
    __metaclass__ = func2static 

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

f = lambda x: x 

X.f = f 
X.f(3) # no exception 

x = X()  
x.g = f 
x.g(3) # also no exception 

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