![](/img/trans.png)
[英]How can I make dir(obj) return non-function members in swig generated python classes?
[英]How can I make an alias to a non-function member attribute in a Python class?
我正在编写 Python 库 API,并且经常遇到这样的情况:我的用户希望为相同的函数和变量使用多个不同的名称。
如果我有一个带有函数foo()
的 Python 类,并且我想为它创建一个名为bar()
的别名,那非常简单:
class Dummy:
def __init__(self):
pass
def foo(self):
pass
bar = foo
现在我可以毫无问题地做到这一点:
d = Dummy()
d.foo()
d.bar()
我想知道的是使用作为常规变量(例如字符串)而不是函数的类属性执行此操作的最佳方法是什么? 如果我有这段代码:
d = Dummy()
print(d.x)
print(d.xValue)
我希望dx
和d.xValue
总是打印相同的东西。 如果dx
改变,它也应该改变d.xValue
(反之亦然)。
我可以想到很多方法来做到这一点,但没有一个看起来像我想要的那样顺利:
@property
注释并弄乱setter__setattr__
类函数这些方法中哪一种最好? 还是有其他方法? 我不禁觉得,如果为函数制作别名是如此容易,那么对于任意变量也应该同样容易......
这可以通过与类方法完全相同的方式来解决。 例如:
class Dummy:
def __init__(self):
self._x = 17
@property
def x(self):
return self._x
@x.setter
def x(self, inp):
self._x = inp
@x.deleter
def x(self):
del self._x
# 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)
这两个值将始终保持同步。 您使用您喜欢的属性名称编写实际的属性代码,然后使用您需要的任何旧名称对其进行别名。
您可以提供引用别名映射的__setattr__
和__getattr__
:
class Dummy:
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 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
当你的一半用户决定使用dx
而另一半决定使用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
但是基于上面提到的原因,我认为这不是一个好的设计。 它使 Dummy 更难阅读、理解和使用。 对于每个用户,您都将 API 的大小增加了一倍,用户必须知道才能理解 Dummy。
更好的选择是使用适配器设计模式。 这使您可以保持 Dummy 美观、紧凑、简洁:
class Dummy(object):
def __init__(self):
self.x=1
虽然子域中希望使用不同词汇表的用户可以通过使用 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
对于 Dummy 中的每个方法和属性,您只需连接类似的方法和属性,将繁重的工作委托给 Dummy 的实例。
它可能是更多的代码行,但它可以让你为 Dummy 保留一个干净的设计,更容易维护、记录和单元测试。 人们会编写有意义的代码,因为类将限制可用的 API,并且给定他们选择的类,每个概念只有一个名称。
您可以使用 ActiveState Python 配方中显示的一些想法,标题为Caching and aliasing with descriptors 。 这是那里显示的代码的简明版本,它提供了您寻求的功能。
编辑:可以使包含Alias
属性的类在您del
一个时自动删除任何关联的目标属性(反之亦然)。 我的答案的代码现在说明了一种简单的方法,可以使用方便的类装饰器完成此操作,该装饰器添加自定义__delattr__()
以在可能涉及属性Alias's
时进行专门的删除管理。
class Alias(object):
""" Descriptor to give an attribute another name. """
def __init__(self, name):
self.name = name
def __get__(self, inst, cls):
if inst is None:
return self # a class attribute reference, return this descriptor
return getattr(inst, self.name)
def __set__(self, inst, value):
setattr(inst, self.name, value)
def __delete__(self, inst):
delattr(inst, self.name)
def AliasDelManager(cls):
""" Class decorator to auto-manage associated Aliases on deletion. """
def __delattr__(self, name):
""" Deletes any Aliases associated with a named attribute, or
if attribute is itself an Alias, deletes the associated target.
"""
super(cls, self).__delattr__(name) # Use base class' method.
for attrname in dir(self):
attr = getattr(cls, attrname)
if isinstance(attr, Alias) and attr.name == name:
delattr(cls, attrname)
setattr(cls, '__delattr__', __delattr__)
return cls
if __name__=='__main__':
@AliasDelManager
class Dummy(object):
def __init__(self):
self.x = 17
xValue = Alias('x') # create an Alias for attr 'x'
d = Dummy()
assert d.x == 17
assert d.xValue == 17
d.x = 23
assert d.xValue == 23
d.xValue = 1492
assert d.x == 1492
assert d.x is d.xValue
del d.x # should also remove any associated Aliases
assert 'xValue' not in dir(d)
print('done - no exceptions were raised')
此函数将属性名称作为参数并返回一个属性,该属性用作获取和设置的别名。
def alias_attribute(field_name: str) -> property:
"""
This function takes the attribute name of field to make a alias and return
a property that work to get and set.
"""
field = property(lambda self: getattr(self, field_name))
field = field.setter(lambda self, value: setattr(self, field_name, value))
return field
例子:
>>> class A:
... name_alias = alias_attribute('name')
... def __init__(self, name):
... self.name = name
... a = A('Pepe')
>>> a.name
'Pepe'
>>> a.name_alias
'Pepe'
>>> a.name_alias = 'Juan'
>>> a.name
'Juan'
覆盖__getattr__()
方法并返回适当的值。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.