def inner(cls):
class wrapper():
def init(self,args,**kargs):
self.warp = cls(args,**kargs)
return wrapper
class stu():
def init(self,name,age):
self.name = name
self.age = age
def get(self):
print(self.name,self.age)
if name == "main":
stu = inner(stu)
stu = stu("xiaming",12)
stu.get()
我本想通过装饰器来装饰类,但是报:
stu.get()
^^^^^^^
AttributeError: 'wrapper' object has no attribute 'get'
这是什么原因,我需要怎么改
我们调用stu.get()
,但是wrapper
的实例并没有get
方法,因为wrapper
类只定义了__init__
方法,所以会报错:'wrapper' object has no attribute 'get'
。
我们想要的效果是,装饰后的类在调用方法时能够像原类一样工作。这里装饰器返回的是一个新的类wrapper
,而原类的方法并没有被代理
我们可以让wrapper
类代理所有对原类实例的属性和方法访问。这可以通过在wrapper
类中实现__getattr__
方法来实现。这样,当访问wrapper
实例上不存在的属性或方法时,会尝试访问其内部包裹的self.warp
(即原类的实例)的相应属性或方法。
修改后的代码:
在wrapper
类中添加:
def getattr(self, name):
return getattr(self.warp, name)
这样,当我们调用stu.get()
时,由于wrapper
实例本身没有get
方法,就会调用__getattr__
,然后去self.warp
(即原stu
类的实例)上找get
方法并调用。
def inner(cls):
class wrapper:
def init(self, args, **kargs):
self.wrapped = cls(args, **kargs) # 存储原始类的实例
# 代理所有未定义的方法到原始类的实例
def __getattr__(self, name):
return getattr(self.wrapped, name)
return wrapper
class stu:
def init(self, name, age):
self.name = name
self.age = age
def get(self):
print(self.name, self.age)
if name == "main":
stu = inner(stu) # 装饰原始类
s = stu("xiaming", 12) # 创建装饰后的实例
s.get() # 正确调用:输出 xiaming 12
注意:通过__getattr__
只能转发实例属性和方法,对于类属性(比如在类上直接调用的)或者特殊方法(如__str__
等)可能不会按预期工作。因为__getattr__
只在查找实例属性时被调用,而不会在查找类属性时被调用。另外,一些特殊方法(如__len__
,__str__
等)通常是在类上查找,而不是在实例上查找,所以不会经过__getattr__
。因此,如果需要这些特殊方法,可能需要单独在wrapper
类中实现,或者使用其他方法(如继承)。
不过,对于普通方法,这样是可行的。
其他实现方式(可选):
方法二:使用继承(更简洁)
python
def inner(cls):
class wrapper(cls): # 直接继承原始类
def init(self, args, **kargs):
super().init(args, **kargs)
return wrapper
优点:自动继承所有方法,无需手动代理。
缺点:可能意外覆盖父类行为。
getattr 方法的作用
python
def getattr(self, name):
return getattr(self.warp, name)
这是 Python 的特殊方法
当访问对象上不存在的属性/方法时自动调用
name 参数是要访问的属性/方法名
getattr() 函数的作用
python
getattr(self.warp, name)
内置函数,等价于 self.warp.name
动态获取对象属性/方法
如果 name 是方法,返回的是绑定方法(bound method)
为什么这个代理有效
当您调用 stu.get() 时(这里的 stu 是 wrapper 实例):
Python 首先在 wrapper 实例上查找 get 方法
因为 wrapper 类没有定义 get 方法,触发 getattr
getattr 执行:getattr(self.warp, "get")
这会:
在 self.warp(原始 stu 类的实例)上查找 get 方法
找到后返回这个绑定方法(已经绑定了 self.warp 实例)
返回的方法被调用:get() → 实际执行 self.warp.get()
这种代理方式本质上是将调用转发给内部包含的原始类实例
这种模式在 Python 中很常见,称为:
代理模式 (Proxy Pattern)
装饰器模式 (Decorator Pattern)
适配器模式 (Adapter Pattern)
getattr
函数:它是Python内置函数,用于获取对象的属性。它的作用等同于点号(.)访问,但可以动态指定属性名。例如:
getattr(obj, 'name') # 相当于 obj.name
getattr(obj, 'method') # 相当于 obj.method
因此,__getattr__
方法中的return getattr(self.warp, name)
的意思就是:返回self.warp
对象的名为name
的属性。
def inner(cls):
class wrapper():
def init(self,args,**kargs):
self.warp = cls(args,**kargs)
def getattr(self,name):
return getattr(self.warp,name)
return wrapper
@inner
class stu():
def init(self,name,age):
self.name = name
self.age = age
def get(self):
print(self.name,self.age)
if name == "main":
stu = stu("xiaming",12)
stu.get()