假设我有两个类:
- 一个名为
Swimmer
的类- 一个名为
Person
的类
对于我的特定应用程序,我们可以NOT让Swimmer
从Person
继承,尽管我们想要类似继承的东西。
每个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'>"