对包装器类使用' __getattr__() '方法的替代方法是什么?



假设我有两个类:

  1. 一个名为Swimmer的类
  2. 一个名为Person的类

对于我的特定应用程序,我们可以NOTSwimmerPerson继承,尽管我们想要类似继承的东西。

每个Swimmer都有一个Person类的实例作为成员变量,而不是类继承。

class Person:
pass
class Swimmer:
def __init__(self, person):  
self._person = person 
def __getattr__(self, attrname:str):
try:
attr = getattr(self._person)
return attr  
except AttributeError:
raise AttributeError    

也许Person类有以下类方法:

  • kneel()
  • crawl()
  • walk()
  • lean_over()
  • lay_down()

Swimmer类具有与Person类相同的所有方法,加上一些额外的方法:

  • run()
  • swim()
  • dive()
  • throw_ball()

当涉及到跪,爬,走,躺,Swimmer意味着是一个透明的包装在Person类。


我想这样写:

swimmer_instance = SwimmerClass(person_instance)

我写了一个__getattr__()方法。

然而,我在使用__getattr__()时遇到了很多麻烦。

考虑编写代码self.oops_Swimmer类没有名为oops的属性。我们不应该在self._person中寻找oops

每当我输入错误的Swimmer的属性名称时,我的计算机就会在Person类的实例中搜索该属性。通常,纠正这样的拼写错误很容易。但是,使用__getattr__()方法,追踪问题变得困难。

我怎样才能避免这个问题?


也许一个选择是创建Swimmer类的子类。在子类中有一个方法,其名称是__getattr__的拼写错误。然而,我不确定这个想法;请告诉我。

class _Swimmer:
def __init__(self, person):  
self._person = person 
def run(self):
return "I ran"
def swim(self):
return "I swam"
def dive(self):
# SHOULD NOT LOOK FOR `oops` inside of self._person!
self.oops   
return "I dove"
def _getattrimp(self, attrname:str):
# MISSPELLING OF `__getattr__`
try:
attr = getattr(self._person)
return attr  
except AttributeError:
raise AttributeError  
class Swimmer(_Swimmer):
def __getattr__(self, attrname:str):
attr = self._getattrimp(attrname)
return attr

真的,对我来说重要的是我们而不是查看self._person内部,除了以下内容:

  • Kneel()
  • Crawl()
  • Walk()
  • Lean()
  • LayDown()

解决方案必须更通用,而不仅仅是适用于Swimmer类和Person类。

我们如何编写一个函数,它接受任何类作为输入,并弹出一个具有与输入类同名方法的类?

我们可以通过写入person_attributes = dir(Person)得到Person属性的列表。

是否适合动态创建一个以Person为输入的Swimmer子类?

class Person:  
def kneel(self, *args, **kwargs):
return "I KNEELED"
def crawl(self, *args, **kwargs):
return "I crawled"
def walk(self, *args, **kwargs):
return "I WALKED"
def lean_over(self, *args, **kwargs):
return "I leaned over"
################################################################
import functools
class TransparentMethod:
def __init__(self, mthd):  
self._mthd = mthd 
@classmethod
def make_transparent_method(cls, old_method):
new_method = cls(old_method)
new_method = functools.wraps(old_method)
return new_method  
def __call__(self, *args, **kwargs):
ret_val = self._mthd(*args, **kwargs)   
return ret_val  
###############################################################
attributes = dict.fromkeys(dir(Person))
for attr_name in attributes.keys():
old_attr = getattr(Person, attr_name)
new_attr = TransparentMethod.make_transparent_method(old_attr)
name       = "_Swimmer"
bases      = (object, )
_Swimmer = type(name, bases, attributes) 
  
class Swimmer(_Swimmer):
pass

如果我对你的问题理解正确的话,你需要一个将两个类合并为一个的函数。

我这样做的方式是用3个参数type()构造函数创建一个空白的容器类,然后循环传递给函数的每个类,使用setattr设置容器类的新属性。我不得不将__class____dict__属性列入黑名单,因为Python不允许更改这些属性。注意,这个函数将覆盖以前添加的方法,比如__init__()方法,所以最后传递构造函数的类。

我在下面的combineClasses函数中实现了这一点。我还提供了一个例子。在这个例子中,我创建了一个基本的Person类和一个_Swimmer类。我在这两个类上调用combineClasses,并将结果类存储为Swimmer,因此它可以很好地作为包装器类调用。

def combineClasses(name, *args):
container = type(name, (object,), {})
reserved = ['__class__', '__dict__']
for arg in args:
for method in dir(arg):
if method not in reserved:
setattr(container, method, getattr(arg, method))
return container

class Person:
def __init__(self, name):
self.name = name
def sayHi(self):
print(f'Hi, I am {self.name}')

class _Swimmer:
def swim(self):
print('I am swimming')

class _Cashier:
def work(self):
print(f'I am working! My name is {self.name}')

Swimmer = combineClasses('Swimmer', _Swimmer, Person)
bob = Swimmer('Bob')
bob.swim()  # => "I am swimming"
bob.sayHi()  # => "Hi, I am Bob"
print(bob.name)  # => "Bob"
print(type(bob))  # => "<class '__main__.Swimmer'>"

相关内容

最新更新