我在Python中有一个继承链,我希望每个子类能够添加新的自定义参数。现在我正在这样做:
class A(object):
PARAM_NAMES = ['blah1']
...
class B(A):
PARAM_NAMES = A.PARAM_NAMES + ['blah2']
...
我想知道是否有一个更流畅的方法,但没有引用A
两次?无法使用super()
,因为它不在方法定义中,afaik。我想我可以使用类方法,但这很烦人(因为我真的想要一个属性)。
正确的方法是什么?
答案 0 :(得分:2)
粗糙总有黑魔法可以做......但问题只是因为你可以...... 你应该吗?
class MyMeta(type):
items = []
def __new__(meta, name, bases, dct):
return super(MyMeta, meta).__new__(meta, name, bases, dct)
def __init__(cls, name, bases, dct):
MyMeta.items.extend(cls.items)
cls.items = MyMeta.items[:]
super(MyMeta, cls).__init__(name, bases, dct)
class MyKlass(object):
__metaclass__ = MyMeta
class A(MyKlass):
items=["a","b","c"]
class B(A):
items=["1","2","3"]
print A.items
print B.items
因为这会创建一个副本,所以它不会遇到与其他解决方案相同的问题
(请注意,我真的不建议这样做......它只是为了表明你可以)
答案 1 :(得分:2)
这可能是也可能不是很聪明,但技术上可以使用元类来实现。与Joran的方法不同,我使用了一个属性,因此它保留了完整的动态特性(也就是说,如果在定义类之后修改任何类的私有_PARAM_NAMES
列表,则每个其他派生类的相应PARAM_NAME
属性反映了这种变化)。出于这个原因,我在基类上放了一个add_param
方法。
此处假设使用Python 3,PARAM_NAMES
属性返回set
以避免重复项目。
class ParamNameBuilderMeta(type):
def __new__(mcl, name, bases, dct):
names = dct.get("PARAM_NAMES", [])
names = {names} if isinstance(names, str) else set(names)
dct["_PARAM_NAMES"] = names
dct["PARAM_NAMES"] = property(lambda s: type(s).PARAM_NAMES)
return super().__new__(mcl, name, bases, dct)
@property
def PARAM_NAMES(cls):
# collect unique list items ONLY from our classes in the MRO
return set().union(*(c._PARAM_NAMES for c in reversed(cls.__mro__)
if isinstance(c, ParamNameBuilderMeta)))
用法:
class ParamNameBuilderBase(metaclass=ParamNameBuilderMeta):
@classmethod
def add_param(self, param_name):
self._PARAM_NAMES.add(param_name)
class A(ParamNameBuilderBase):
PARAM_NAMES = 'blah1'
class B(A):
PARAM_NAMES = 'blah1', 'blah2'
class C(B):
pass
检查以确保它适用于类和实例:
assert C.PARAM_NAMES == {'blah1', 'blah2'}
assert C().PARAM_NAMES == {'blah1', 'blah2'}
检查以确保它仍然是动态的:
C.add_param('blah3')
assert C.PARAM_NAMES == {'blah1', 'blah2', 'blah3'}
答案 2 :(得分:1)
您所描述的行为实际上非常具体。你说过你
希望每个子类能够添加新的自定义参数
但是你实现它的方式,这将导致不可预测的行为。考虑:
class A(object):
PARAM_NAMES = ['blah1']
class B(A):
PARAM_NAMES = A.PARAM_NAMES + ['blah2']
class C(A):pass
print(A.PARAM_NAMES)
print(B.PARAM_NAMES)
print(C.PARAM_NAMES)
A.PARAM_NAMES.append('oops')
print(C.PARAM_NAMES)
我们注意到,选择添加新参数的类具有对参数列表的新引用,而不添加新参数的类具有与其父参数相同的引用。除非小心控制,否则这是不安全的行为。
仅将常量用作类属性更为可靠,或者每次都完全重新定义列表(使其成为元组),这不是" slicker"。否则,我会按照你的建议推荐类方法,并使属性成为实例变量