如何在Python类中为非函数成员属性创建别名?

Bre*_*ode 18 python alias class

我正在编写Python库API,我经常遇到我的用户想要相同函数和变量的多个不同名称的场景.

如果我有一个带有该函数的Python类,foo()并且我想为它调用别名,那就bar()太容易了:

class Dummy(object):

   def __init__(self):
      pass

   def foo(self):
      pass

   bar = foo
Run Code Online (Sandbox Code Playgroud)

现在我可以毫无问题地做到这一点:

d = Dummy()
d.foo()
d.bar()
Run Code Online (Sandbox Code Playgroud)

我想知道的是,使用作为常规变量(例如字符串)的类属性而不是函数的最佳方法是什么?如果我有这段代码:

d = Dummy()
print d.x
print d.xValue
Run Code Online (Sandbox Code Playgroud)

我想d.xd.xValueALWAYS打印同样的事情.如果d.x改变,它也应该改变d.xValue(反之亦然).

我可以想到很多方法可以做到这一点,但它们都没有像我想的那样顺利:

  • 编写自定义注释
  • 使用@property注释并弄乱setter
  • 覆盖__setattr__类函数

以下哪种方式最好?或者还有另一种方式吗?我不禁觉得,如果为函数创建别名很容易,那对于任意变量来说应该很容易......

仅供参考:我使用的是Python 2.7.x,而不是Python 3.0,因此我需要一个兼容Python 2.7.x的解决方案(尽管如果Python 3.0可以直接解决这个问题,我会感兴趣).

谢谢!

Ned*_*der 18

您可以提供一个__setattr____getattr__该引用别名映射:

class Dummy(object):
    aliases = {
        'xValue': 'x',
        'another': 'x',
        }

    def __init__(self):
        self.x = 17

    def __setattr__(self, name, value):
        name = self.aliases.get(name, name)
        object.__setattr__(self, name, value)

    def __getattr__(self, name):
        if name == "aliases":
            raise AttributeError  # http://nedbatchelder.com/blog/201010/surprising_getattr_recursion.html
        name = self.aliases.get(name, name)
        #return getattr(self, name) #Causes infinite recursion on non-existent attribute
        return object.__getattribute__(self, name)


d = Dummy()
assert d.x == 17
assert d.xValue == 17
d.x = 23
assert d.xValue == 23
d.xValue = 1492
assert d.x == 1492
Run Code Online (Sandbox Code Playgroud)

  • 重要附录:在`__getattr__` 内使用`getattr` 实际上会导致对不存在属性的无限递归(例如,对于上面的示例,尝试执行`print d.asdf`)。我相信使用 `object.__getattribute__` 可以解决问题。 (2认同)

sma*_*rgh 10

除非我误解了这个问题,否则这几乎可以用类方法解决.

例如,

class Dummy(object):

    def __init__(self):
        self._x = 17

    @property
    def x(self):
        return self._x

    @x.setter
    def x(self, inp):
        self._x = inp

    # Alias
    xValue = x

d = Dummy()
print(d.x, d.xValue)
#=> (17, 17)
d.x = 0
print(d.x, d.xValue)
#=> (0, 0)
d.xValue = 100
print(d.x, d.xValue)
#=> (100, 100)
Run Code Online (Sandbox Code Playgroud)

这两个值将始终保持同步.您使用您喜欢的属性名称编写实际属性代码,然后使用您需要的任何旧名称对其进行别名.

在我眼里,这个代码更易于阅读,比所有的理解__setattr____getattr__覆盖.


unu*_*tbu 5

当你的一半用户决定使用d.x另一半时,你打算做什么d.xValue?当他们尝试共享代码时会发生什么?当然,如果你知道所有的别名,它会起作用,但它会显而易见吗?当你把你的代码放一年时,你会明白吗?

最后,我认为这种善意或奢侈是一种邪恶的陷阱,最终会导致更多的混乱而不是好的.


这主要是因为我的脚本API用于多个子系统和域,因此默认词汇表会发生变化.在一个域中所谓的"X"在另一个域中称为"Y".

您可以通过以下方式为属性创建别名:

class Dummy(object):
   def __init__(self):
      self.x=1
   @property
   def xValue(self):
      return self.x
   @xValue.setter
   def xValue(self,value):
      self.x=value

d=Dummy()
print(d.x)
# 1
d.xValue=2
print(d.x)
# 2
Run Code Online (Sandbox Code Playgroud)

但由于上述原因,我认为这不是一个好的设计.它使Dummy更难阅读,理解和使用.对于每个用户,您已经将用户必须知道的API大小加倍,以便了解Dummy.

更好的选择是使用适配器设计模式.这使您可以保持Dummy漂亮,紧凑,简洁:

class Dummy(object):
   def __init__(self):
      self.x=1
Run Code Online (Sandbox Code Playgroud)

虽然子域中希望使用不同词汇表的用户可以通过使用Adapter类来实现:

class DummyAdaptor(object):
   def __init__(self):
      self.dummy=Dummy()
   @property
   def xValue(self):
      return self.dummy.x
   @xValue.setter
   def xValue(self,value):
      self.dummy.x=value    
Run Code Online (Sandbox Code Playgroud)

对于Dummy中的每个方法和属性,您只需挂钩类似的方法和属性,将繁重的工作委托给Dummy实例.

它可能是更多的代码行,但它将允许您为Dummy保留一个干净的设计,更容易维护,文档和单元测试.人们会编写有意义的代码,因为类会限制可用的API,并且在给定他们选择的类的情况下,每个概念只有一个名称.

  • 我并不一定反对你,但我已经和那场战斗失败了.我能做的下一个最好的事情就是尝试实施和记录好它,因为它可以在一年之内变得清晰. (2认同)