类装饰问题,或者:Python如何区分方法和静态方法? (我需要一位Python 3内部专家)
我有一个类decorator,它修改一些函数,但不修改其他函数。 简化示例:类装饰问题,或者:Python如何区分方法和静态方法? (我需要一位Python 3内部专家),python,python-3.x,decorator,Python,Python 3.x,Decorator,我有一个类decorator,它修改一些函数,但不修改其他函数。 简化示例: import functools import inspect import types def mydecorator(myobj): @functools.wraps(myobj) def decorated_method(*args, **kwargs): print("I'm decorated!") return myobj(*args, **kwargs)
import functools
import inspect
import types
def mydecorator(myobj):
@functools.wraps(myobj)
def decorated_method(*args, **kwargs):
print("I'm decorated!")
return myobj(*args, **kwargs)
if inspect.isclass(myobj): # act as class decorator
for name, obj in myobj.__dict__.items():
if name == "add":
setattr(myobj, name, types.MethodType(mydecorator(obj), myobj))
return myobj # return decorated class
elif inspect.isfunction(myobj): # act as function decorator
return decorated_method
else:
assert False, "can decorate only classes and functions"
因此,这将修改任何add
方法,以便在运行之前打印“我被装饰”
我们将其应用于此类:
class MyClass:
def add(self, x, y): return x + y
def mul(self, x, y): return x * y
它工作正常。是的
#--- try out undecorated MyClass:
print("MyClass.add:", MyClass.add, "MyClass.mul:", MyClass.mul)
print("3+4 =", MyClass().add(3, 4), "3*4 =", MyClass().mul(3, 4), )
#--- decorate MyClass:
print("MyClass = mydecorator(MyClass)")
MyClass = mydecorator(MyClass)
#--- try out decorated MyClass in the same manner:
print("MyClass.add:", MyClass.add, "MyClass.mul:", MyClass.mul)
print("3+4 =", MyClass().add(3, 4), "3*4 =", MyClass().mul(3, 4), )
并获取此输出(来自Linux上的CPython 3.6.7)
输出变为:
MyClass.add: <function MyClass.add at 0x7fbc760870d0> MyClass.mul: <function MyClass.mul at 0x7fbc76087158>
3+4 = 7 3*4 = 12
MyClass = mydecorator(MyClass)
MyClass.add: <bound method MyClass.add of <class '__main__.MyClass'>> MyClass.mul: <function MyClass.mul at 0x7fbc76087158>
I'm decorated!
Traceback (most recent call last):
File "tryout.py", line 34, in <module>
print("3+4 =", MyClass().add(3, 4), "3*4 =", MyClass().mul(3, 4), )
File "tryout.py", line 16, in decorated_method
return myobj(*args, **kwargs)
File "tryout.py", line 7, in add
def add(self, x, y): z = self.mul(x, y); return x + y # round 2
TypeError: mul() missing 1 required positional argument: 'y'
MyClass.add:MyClass.mul:
3+4 = 7 3*4 = 12
MyClass=mydecorator(MyClass)
MyClass.add:MyClass.mul:
我被装饰了!
回溯(最近一次呼叫最后一次):
文件“tryout.py”,第34行,在
打印(“3+4=,MyClass().add(3,4),“3*4=,MyClass().mul(3,4),)
文件“tryout.py”,第16行,采用修饰的方法
返回myobj(*args,**kwargs)
文件“tryout.py”,第7行,添加
def add(self,x,y):z=self.mul(x,y);返回x+y#第2轮
TypeError:mul()缺少1个必需的位置参数:“y”
事实证明,mul
(尽管它与以前一样!)现在被调用,就好像它是一个@staticmethod
:self
没有被传递一样
我有很多问题:
add
绑定到哪个对象@classmethod
或@staticmethod
types.MethodType
真正的意思是什么问题是不应该用绑定方法替换函数
add
。方法的工作方式是函数
对象有一个\uuuuu get\uuuuu
方法,在实例方法的情况下,该方法返回一个绑定方法,供您在提供的参数上调用。也就是说
class MyClass:
def add(self, x, y):
return x + y
def mul(self, x, y):
return x * y
o = MyClass()
像o.add(3,5)
这样的调用相当于type(o)。\uuu dict\uuu['add']。\uu get\uuu(o,type(o))(3,5)
您的装饰程序还应该简单地返回一个新函数,而不是方法
对象,并让它的\uuuu get\uuu
方法完成它的工作
您的新装饰师,经过一些简化:
def mydecorator(myobj):
@functools.wraps(myobj)
def decorated_method(*args, **kwargs):
print("I'm decorated!")
return myobj(*args, **kwargs)
# Decorating a function
if inspect.isfunction(myobj):
return decorated_method
# Decorating a class
if inspect.isclass(myobj):
if "add" in myobj.__dict__:
setattr(myobj, "add", mydecorator(obj))
# Or just setattr(myobj, "add", decorated_method),
# unless you think myobj.add might be a nested class
return myobj
# Anything else is type error.
raise TypeError("can decorate only classes and functions")
解决您的其他一些问题 Python如何在内部区分普通方法与@classmethod或@staticmethod
classmethod
和staticmethod
对象返回的对象与常规的函数
对象具有不同的\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu方法
我在哪里可以找到所有这些文件
这是一个很好的开始。它描述了描述符协议,以及属性和方法等如何使用它的示例。补充@chepner的伟大答案,并回答您的第4点
types.MethodType真正的意思是什么
这种特殊类型允许我们向已经创建的实例添加方法,在您的例子中没有实例,因此当您将myobj
传递给它时,您基本上是将包装的\uuuuu self\uuuu
设置到它的类中,而不是使用它的实例
让我们来看一个更简单的类版本,没有decorator:
class MyClass:
def add(self, x, y):
z = self.mul(x, y);
return x + y
def mul(self, x, y): return x * y
现在:
现在当我们这样做的时候,你做了什么:
>>> MyClass.add = types.MethodType(ins.add.__func__, MyClass)
现在将类传递给函数add
,而不是实例:
>>> ins = MyClass()
>>> ins.add.__self__
<class '__main__.MyClass'>
请注意,这回答了问题1、3、6和(隐式)5。
>>> ins = MyClass()
>>> ins.add.__func__
<function MyClass.add at 0x7f483050cf28>
>>> ins.add.__self__
<__main__.MyClass object at 0x7f48304ef390>
>>> ins.add(1, 2)
3
>>> ins.add.__func__(ins.add.__self__, 1, 2)
3
>>> MyClass.add = types.MethodType(ins.add.__func__, MyClass)
>>> ins = MyClass()
>>> ins.add.__self__
<class '__main__.MyClass'>
>>> MyClass.mul(1, 2)
...
TypeError: mul() missing 1 required positional argument: 'y'