Преобразовать встроенный тип функции в тип метода (в Python 3)

Рассмотрим простую функцию типа

def increment(self): self.count += 1 

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

 class Counter: def __init__(self): self.count = 0 from compiled_extension import increment Counter.increment = increment 

Теперь это не сработает, так как соглашение о вызове на уровне C будет нарушено. Например:

 >>> c = Counter() >>> c.increment() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: increment() takes exactly one argument (0 given) 

Но в Python 2 мы можем преобразовать функцию в несвязанный метод, выполнив:

 Counter.increment = types.MethodType(increment, None, Counter) 

Как я могу выполнить то же самое в Python 3?

Один простой способ – использовать тонкую обертку:

 from functools import wraps def method_wraper(f): def wrapper(*args, **kwargs): return f(*args, **kwargs) return wraps(f)(wrapper) Counter.increment = method_wrapper(increment) 

Есть ли более эффективный способ сделать это?

2 Solutions collect form web for “Преобразовать встроенный тип функции в тип метода (в Python 3)”

Первое, что правильно называют имена:

 >>> def increment(obj): ... obj.count += 1 ... >>> class A(object): ... def __init__(self): ... self.count = 0 ... >>> o = A() >>> o.__init__ <bound method A.__init__ of <__main__.A object at 0x0000000002766EF0>> >>> increment <function increment at 0x00000000027797C8> 

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

В общем, дескриптор является атрибутом объекта с «поведением привязки», тот, чей доступ к атрибуту был переопределен методами в протоколе дескриптора. Этими методами являются __get__ , __set__ и __delete__ . Если какой-либо из этих методов определен для объекта, он называется дескриптором.

Вы можете легко преобразовать функцию в метод, просто используя разные вызовы __get__

 >>> increment.__get__(None, type(None)) <function increment at 0x00000000027797C8> >>> increment.__get__(o, type(o)) <bound method A.increment of <__main__.A object at 0x00000000027669B0>> 

И это работает как шарм:

 >>> o = A() >>> increment.__get__(None, type(None))(o) >>> o.count 1 >>> increment.__get__(o, type(o))() >>> o.count 2 

Вы можете легко добавить эти новые ограниченные методы к объектам:

 def increment(obj): obj.count += 1 def addition(obj, number): obj.count += number class A(object): def __init__(self): self.count = 0 o = A() o.inc = increment.__get__(o) o.add = addition.__get__(o) print(o.count) # 0 o.inc() print(o.count) # 1 o.add(5) print(o.count) # 6 

Или создайте свой собственный дескриптор , который преобразует функцию в связанный метод :

 class BoundMethod(object): def __init__(self, function): self.function = function def __get__(self, obj, objtype=None): print('Getting', obj, objtype) return self.function.__get__(obj, objtype) class B(object): def __init__(self): self.count = 0 inc = BoundMethod(increment) add = BoundMethod(addition) o = B() print(o.count) # 0 o.inc() # Getting <__main__.B object at 0x0000000002677978> <class '__main__.B'> print(o.count) # 1 o.add(5) # Getting <__main__.B object at 0x0000000002677978> <class '__main__.B'> print(o.count) # 6 

И вы также можете видеть, что это хорошо согласуется с принципами функции / связанного метода :

Классовые словари хранят методы как функции. В определении класса методы записываются с использованием def и lambda, обычных инструментов для создания функций. Единственное отличие от обычных функций состоит в том, что первый аргумент зарезервирован для экземпляра объекта. По соглашению Python ссылка экземпляра называется self, но может быть вызвана этим или любым другим именем переменной.

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

И функции становятся связанными методом во время инициализации экземпляра:

 >>> B.add # Getting None <class '__main__.B'> <function addition at 0x00000000025859C8> >>> o.add # Getting <__main__.B object at 0x00000000030B1128> <class '__main__.B'> <bound method B.addition of <__main__.B object at 0x00000000030B1128>> 

Импортируйте расширение следующим образом:

 import compiled_extension 

В своем классе вы пишете:

 def increment: return compiled_extension.increment() 

Это кажется более читаемым и может быть более эффективным.

  • Python: словарь как переменная экземпляра
  • Не следует ли __metaclass__ принудительно использовать метакласс в Python?
  • Объект карты Python не подлежит расшифровке
  • Импорт другого проекта в виде модулей в python
  • OpenCV для Python 3.x под Windows
  • Python NameError: имя не определено
  • `xrange (2 ** 100)` -> OverflowError: long int too large для преобразования в int
  • СинтаксисError не исключается в Python 3
  • Для цикла и 'numpy.float64' объект не является итерируемой ошибкой
  • Как использовать несколько версий Python без удаления
  • Использование python для запуска другой программы?
  • Python - лучший язык программирования в мире.