python:类属性/变量继承与多态?

Don*_*ion 7 python singleton class

在我作为一个python-apprentice的努力中,如果我尝试使用类属性,我最近陷入了一些奇怪的(从我的角度来看)行为.我不是在抱怨,但是会感谢一些有用的评论来阐明这个问题.

为了将复杂的问题简化为一个更简洁的问题,我会像这样制定:

什么是"pythonic"方法来确保类属性在继承树中的行为更像静态变量?

在我看来,类属性的行为类似于具有多态特征的"读取时复制"默认值.只要我执行"只读"操作它就会保持"单例",但是很快,当我通过派生类或实例访问带有赋值的class-attribute时,它会变成一个新的引用,从而失去与继承了base-reference.

(它肯定有一些有趣的功能,但你必须理解它接受它,所以一些洞察力高度赞赏.)

class A(object):
    classvar = 'A'
    def setclassvar(self, value):
        A.classvar = value                   
    def __str__(self):
        return "%s id(%s) " %(A.classvar, hex(id(A.classvar))[2:-1].upper())

class A1(A):
    pass

class B(object):
    classvar = 'B'
    def setclassvar(self, value):
        self.__class__.classvar = value            
    def __str__(self):
        cvar = self.__class__.classvar
        return "%s id(%s) " %(cvar, hex(id(cvar))[2:-1].upper())

class B1(B):
    def setclassvar(self, value):
        self.__class__.classvar = value

a, a1 = A(), A1()
a1.setclassvar('a')
print "new instance A: %s" %a
print "new instance A1: %s" %a

b, b1 = B(), B1()
b1.setclassvar('bb')
print "new instance B: %s" %b
print "new instance B1: %s" %b1

a1.setclassvar('aa')
print "new value a1: %s" %a
print "new value a: %s" %a

a1.classvar = 'aaa'
print "direct access a1: %s id(%s)" %(a1.classvar, hex(id(a1.classvar))[2:-1].upper())
print "method access a1: %s" %a1
print "direct access a: %s" %a
Run Code Online (Sandbox Code Playgroud)

产生以下内容:

new instance A: a id(B73468A0) 
new instance A1: a id(B73468A0) 
new instance B: B id(B73551C0) 
new instance B1: bb id(AD1BFC) 
new value a1: aa id(AD1BE6) 
new value a: aa id(AD1BE6) 
direct access a1: aaa id(A3A494)
method access a1: aa id(AD1BE6) 
direct access a: aa id(AD1BE6)
Run Code Online (Sandbox Code Playgroud)

因此,直接(分配)访问object.classvar或中介通过self.__class__.classvar都不一样BASECLASS.classvar.

这是范围问题还是完全不同的问题.

期待您的回答和感谢.:-)


编辑:在很短的时间内有一个答案建议使用类描述符,如: 如何创建类属性?.

不幸的是,这似乎不起作用:

class Hotel(Bar):
    def __init__(self):        
        Hotel.bar += 1

hotel = Hotel()
assert hotel.bar == 51
assert hotel.bar == foo.bar
Run Code Online (Sandbox Code Playgroud)

第二个断言失败了!hotel.bar没有引用同一个对象,foo.barhotel.bar引用了其他一些酒店.bar!


第2编辑:我非常清楚单身人士被认为是"反模式",我并不打算使用它们(扩展性地).因此我没有在问题标题中提到它们.即使如此,有很多解决方案讨论并提供单身人士的解决方案,我的问题仍然存在:为什么类变量可以轻易地分离它的参考?Ruby的行为方式对我来说更加自然:http://snippets.dzone.com/posts/show/6649

jsb*_*eno 1

如果实现很难解释,那就是个坏主意。

在本例中,为了完整性,我包含了一个实现,因为这是我在 Python 中喜欢的那种棘手的事情。

因此,下面的代码片段滥用了一些闭包来提出一个满足 OP 需求的类装饰器:一个在派生类中保持“统一”读写的类变量。

此外,作为奖励,我将属性包装在描述符中,这使得属性在实例内也不可更改 - 因此每当在原始类的子类或子类的实例中写入属性时,类属性都会正确更新。

正如 Python 之禅所说:“如果实现很难解释,那就是一个坏主意”——我认为我没有什么更难的了——我们在这里讨论的是作用域动态生成的元类。它工作,但它失去了这个“非Pythonic”代码,因为由于类、元类、闭包和描述符机制的大量使用,它非常神秘。

def SingletonAttrs(**names):
    keys = names.keys()
    def class_decorator(cls):
        class Meta(type):
            def __getattribute__(cls, attr):
                if attr in keys:
                    return type.__getattribute__(owner_cls,  attr)
                return type.__getattribute__(cls, attr)
            def __setattr__(cls, attr, value):
                if attr in keys:
                    class Wrapper(object):
                        def __init__(self, value):
                            self.__set__(None, value)
                        __set__ = lambda self, instance, value: setattr(owner_cls,"__" +  attr, value)
                        __get__ = lambda self, instance, owner: type.__getattribute__(owner_cls, "__" + attr)
                    return type.__setattr__(owner_cls,  attr, Wrapper(value))
                return type.__setattr__(cls, attr, value)
        owner_cls = Meta(cls.__name__, cls.__bases__, cls.__dict__.copy())
        for key in keys:
            setattr(owner_cls, key, names[key])
        return owner_cls
    return class_decorator

if __name__ == "__main__":

    @SingletonAttrs(a="value 1", b="value 2")
    class Test(object):
        pass

    class TestB(Test):
        pass

    t = Test()
    print t.a
    print t.b
    tb = TestB()
    tb.a = "value 3"
    print Test.a
Run Code Online (Sandbox Code Playgroud)