有没有一种方法可以通过只调用类名而调用函数?

时间:2020-03-24 06:00:54

标签: python python-3.x class pygame python-requests

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

pdb.set_trace()

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

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)

1 个答案:

答案 0 :(得分:3)

这是基本组成/授权。此处的规范方法是将委托人设为私有(在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还提供了自动委托的钩子:the __getattr__(self, name) magic method

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)

这种自动委托的缺点是:1 /它可以提供对某些您不想公开的委托人属性的访问; 2 /委托检查既不显式可见也不可以通过检查发现; 3 /您得到一些额外的开销。

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

注意:如果这是代码中的重复模式,您仍然可以使用自定义描述符或自定义元类或类装饰器来设置一些“半自动”委派系统,即(自定义描述符示例): / p>

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)

现在,对于基本上很简单的内容来说,有很多代码和间接级别,因此只有在您确实有很多 lot 可以建立的代表团时才有意义-从经验上看,最简单的代码,它越容易阅读,理解,测试和调试(python zen:“简单胜于复杂”),从长远来看,“智能”解决方案通常会成为PITA(在这里,做到了,现在我知道了)更好)。