【问题标题】:Is there a way by just calling the class's name I call a function?有没有办法只调用类的名称我调用一个函数?
【发布时间】:2020-03-24 06:00:54
【问题描述】:

例如,我正在创建一个定义 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)

【问题讨论】:

  • 你可以在类中添加一个方法。 def move(self, x, y): self.pos.mov(x,y)
  • 请注意move 函数不会改变矩形,而是返回一个新的。也许您正在寻找move_ip

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


【解决方案1】:

这是基本的组合/委托。这里的规范方法是使被委托者私有(在 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/ 您会获得一些额外的开销。

第一个问题可以通过保留您希望允许访问的受委托人属性的白名单来解决。唉,第二点没有简单的解决方案,第三点根本没有解决方案,所以最好还是使用显式委托(至少对于重要部分)并为通用代理类等保持自动委托。

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

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(来过这里,做过那个,现在我知道了)更好)。

【讨论】:

  • 谢谢。我现在正在使用您的第一个明确授权。我一开始就用这个。我想也许有更简单的方法。这绝对是最简单的。谢谢。
  • @Larson 好,重新阅读我的答案我意识到我忘了提到几个可能的“半自动”解决方案 - 以我编辑的答案为例,但请仔细阅读最后的警告 - 这是不是因为某些事情是可能的,并且一开始可以为您节省几次击键,这是一个好主意;-)
猜你喜欢
  • 2021-06-05
  • 2019-02-01
  • 2021-08-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多