Объекты класса нерегулярны при назначении функций - PullRequest
1 голос
/ 13 января 2011

Допустим, у меня есть такая функция:

def id(x):
  return x

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

x.f = id

и уметь делать

x.f(1)
=> 1

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

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

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

Ответы [ 3 ]

3 голосов
/ 13 января 2011
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 это изменяется, и вы просто получаете оригинальную функцию. Я считаю, что это для обратной совместимости с классами старого стиля.)


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

1 голос
/ 13 января 2011

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

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

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

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

1 голос
/ 13 января 2011

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

def id_(self, x):
    return x

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

X.f = id_
x = X()
x.f(1)
=> 1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...