有没有办法只调用类名来调用函数?



例如,我正在创建一个定义pygame矩形的类。

class rect():
def __init__(self,x,y,w,h):
self.pos = pygame.Rect(x,y,w,h)
r = rect(10,10,10,10)
r.pos.move(10,10)

在代码中有没有任何方法可以让我只获取调用self.pos类的变量的名称。这样我就可以删除.pos

r = rect(10,10,10,10)
r.move(10,10)

这是基本的组合/委派。这里的规范方法是使被委派者私有(在Python中:用一个前导下划线前缀其名称-这只是一个惯例,但它是一个非常强的惯例(,并编写公共方法来委派操作,即:

# naming convention: class names should be CamelCase
class Rect():
def __init__(self, x, y, w, h):
# naming convention: the single leading underscore
# denotes an implementation attribute or method
self._pos = pygame.Rect(x, y, w, h)
# explicit delegation  
def move(self, x, y):
self._pos.move(x, y)

r = Rect(10,10,10,10)
r.move(10,10)

现在,如果您有几十个方法要委托,Python还提供了一个用于自动委托的钩子:__getattr__(self, name)魔术方法:

class Rect():
def __init__(self, x, y, w, h):
self._pos = pygame.Rect(x, y, w, h)
# implicit delegation:
def __getattr__(self, name):
try:
return getattr(self._pos, name)
except AttributeError:
# here we hide the fact we're delegating to another object
raise AttributeError(
"'{}'.object has no attribute '{}'".format(
type(self).__name__, name
))

r = Rect(10,10,10,10)
r.move(10,10)

这种automagic委派的缺点是,1/它可以提供对一些您不想公开的被委派属性的访问,2/委派方法既不明确可见,也不可通过检查发现,3/您会获得一些额外的开销。

第一个问题可以通过保留一个你想允许访问的被委派属性的白名单来解决。遗憾的是,第二点没有简单的解决方案,第三点根本没有解决方案,所以最好还是进行显式委派(至少对重要部分(,并对通用代理类等保持自动魔术委派。

注意:如果这是你代码中的一个重新出现的模式,你仍然可以使用自定义描述符、自定义元类或类装饰器来设置一些"半自动魔术"委托系统,即(自定义描述符示例(:

class delegate_to():
def __init__(self, delegatee, attr=None):
self._delegatee = delegatee
self._attr = attr
self._name = None
self._owner = None
def __set_name__(self, owner, name):
self._owner = owner
self._name = name
if self._attr is None:
self._attr = name
def _raise(self):
msg = "'{}' object has no attribute '{}'".format(
self._owner.__name__,
self._attr
)
raise AttributeError(msg)
def __get__(self, obj=None, cls=None):
if obj is None:
return self
delegatee = getattr(obj, self._delegatee)
try:
return  getattr(delegatee, self._attr)
except AttributeError:
self._raise(obj, cls)
def __set__(self, value):
raise AttributeError("Attribute is read-only")
def __repr__(self):
return "<Delegatee {} ({}) object for {}>".format(
self._name, self._attr, self._owner.__name__
)
# exemple use :
class Delegatee():
def __init__(self, x, y):
self.x = x
self.y = y
def move_to(self, x, y):
self.x = x
self.y = y
def move_by(self, x, y):
self.x += x
self.y += y
@property
def position(self):
return self.x, self.y

class Composite():
def __init__(self, x, y):
self._delegatee = Delegatee(x, y)
move_to = delegate_to("_delegatee")
move_by = delegate_to("_delegatee")
position = delegate_to("_delegatee")

您还可以使用类装饰器来进一步自动化,该类装饰器将创建delegate_to描述符:

class delegator():
def __init__(self, delegatee, *names):
if not names:
raise ValueError("needs one or more name to delegate")
self.delegatee = delegatee
self.names = names
def create_delegator(self, owner, name, attrname=None):
if not attrname:
attrname = name
delegator =  delegate_to(self.delegatee, attrname)
delegator.__set_name__(owner, name)
return delegator
def __call__(self, cls):
for name in self.names:
if isinstance(name, tuple):
name, attr = name
else:
attr = name
delegator = self.create_delegator(name, attr)
setattr(cls, name, delegator)
return cls

@delegator("_delegatee", "move_to", "move_by", ("pos", "position"))
class Composite2():
def __init__(self, x, y):
self._delegatee = Delegatee(x, y)

现在,对于基本上非常简单的东西来说,有很多代码和间接级别,所以只有当你真的有很多的委托要设置时,这才有意义——根据经验,最简单的代码越容易阅读、理解、测试和调试(python zen:"简单胜于复杂"(,从长远来看,"智能"解决方案往往会成为一种PITA(来过,做过,现在我更清楚了(。

最新更新