bfr*_*hle 16 python cython python-3.x
考虑一个简单的函数
def increment(self):
self.count += 1
Run Code Online (Sandbox Code Playgroud)
它通过Cython运行并编译成扩展模块.假设现在我想把这个函数作为一个类的方法.例如:
class Counter:
def __init__(self):
self.count = 0
from compiled_extension import increment
Counter.increment = increment
Run Code Online (Sandbox Code Playgroud)
现在这不起作用,因为C级别的调用约定将被打破.例如:
>>> c = Counter()
>>> c.increment()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: increment() takes exactly one argument (0 given)
Run Code Online (Sandbox Code Playgroud)
但是在Python 2中,我们可以通过执行以下操作将函数转换为未绑定的方法:
Counter.increment = types.MethodType(increment, None, Counter)
Run Code Online (Sandbox Code Playgroud)
我怎样才能在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)
Run Code Online (Sandbox Code Playgroud)
有更有效的方法吗?
首先要正确获取名称:
>>> 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>
Run Code Online (Sandbox Code Playgroud)
所以专有名称是函数和绑定方法。现在您可以寻找如何绑定未绑定方法,您可能最终会阅读有关描述符的内容:
一般来说,描述符是一种具有“绑定行为”的对象属性,其属性访问已被描述符协议中的方法覆盖。这些方法是
__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>>
Run Code Online (Sandbox Code Playgroud)
它就像一个魅力:
>>> o = A()
>>> increment.__get__(None, type(None))(o)
>>> o.count
1
>>> increment.__get__(o, type(o))()
>>> o.count
2
Run Code Online (Sandbox Code Playgroud)
您可以轻松地将这些新绑定的方法添加到对象中:
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
Run Code Online (Sandbox Code Playgroud)
或者创建您自己的描述符,将函数转换为绑定方法:
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
Run Code Online (Sandbox Code Playgroud)
您还可以看到,这与函数/绑定方法原则非常一致:
类字典将方法存储为函数。在类定义中,方法是使用 def 和 lambda(创建函数的常用工具)编写的。与常规函数的唯一区别是第一个参数是为对象实例保留的。按照 Python 约定,实例引用称为 self,但也可以称为 this 或任何其他变量名称。
为了支持方法调用,函数包括
__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>>
Run Code Online (Sandbox Code Playgroud)
小智 0
像这样导入扩展:
import compiled_extension
Run Code Online (Sandbox Code Playgroud)
在你的课堂上你写:
def increment: return compiled_extension.increment()
Run Code Online (Sandbox Code Playgroud)
这看起来更具可读性并且可能更有效。
| 归档时间: |
|
| 查看次数: |
2444 次 |
| 最近记录: |