【问题标题】:Class variables initialization in Python3 inheritancePython3继承中的类变量初始化
【发布时间】:2018-01-21 05:45:37
【问题描述】:

我有一个基类A,还有两个子类BC继承自AA中有一些变量可以在所有A对象之间共享,所以我选择使用类变量和这些类变量应该只初始化一次,我可以在A 中通过显式测试和设置A.some_var 来做到这一点。

示例代码(使用显式类名):

from queue import Queue


class A:
    data_queue = None
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if A.data_queue is None:
            A.data_queue = Queue()


class B(A):
    pass


class C(A):
    pass


b = B()
c = C()

b.data_queue.put(5)
c.data_queue.get()  # Works fine

但是,我认为这不是一个好主意,因为在开发过程中可能会更改类名。

我也尝试过type,但通过这种方式,变量在所有B 对象或C 对象之间共享,因为type(self) 返回BC

from queue import Queue


class A:
    data_queue = None
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if type(self).data_queue is None:
            type(self).data_queue = Queue()


class B(A):
    pass


class C(A):
    pass


b = B()
c = C()

b.data_queue.put(5)
c.data_queue.get()  # Block

那么,有没有更好的方法来初始化 A 中的类变量而不需要明确的类名?

【问题讨论】:

  • 你有什么理由不只是用data_queue = Queue()而不是data_queue = None初始化类变量吗?
  • @robert_x44 是的,我可以用data_queue = Queue() 初始化data_queue,但是我还有另一个Process 需要初始化,它需要来自A 基类的一些上下文信息,如果我选择在类体中初始化它,我仍然需要对类名进行硬编码才能访问上下文信息。(由于设计原因,我不能在A 的基类中定义这个Process
  • self.__class__.data_queue 会起作用。可变的类级别状态,或从 instance 构造函数对类级别的东西进行延迟初始化,通常不是一个好主意。在类创建时初始化队列(是的,类主体中的data_queue = Queue()),或者考虑其他共享机制。
  • @9000 是的,data_queue = Queue() 工作正常,但我有这个A.data_proc = Process(target=comsumer, args=(BaseA.context,))BaseAA 的超类,如果我像data_queue 一样初始化data_proc,那么我仍然需要硬编码BaseA.context 之类的东西,因为当时A 对它的超类一无所知,这不好。
  • @Time1ess:在这种情况下,继承似乎不是一种方便的机制。 (构图几乎总是更好。)

标签: python-3.x oop inheritance initialization


【解决方案1】:

经过一番挣扎,我想出了一些解决方案,虽然不是很好,但在某种程度上达到了我的目标。

解决方案 1:使用装饰器设置对类的引用

示例代码:

from queue import Queue


def cls_ref(cls):  # With this decorator, cls refs its self
    cls.cls = cls
    return cls


@cls_ref
class A:
    data_queue = None
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        cls = self.cls
        if cls.data_queue is None:
            cls.data_queue = Queue()


class B(A):
    pass

class C(A):
    pass


b = B()
c = C()

b.data_queue.put(5)
c.data_queue.get()  # Works fine

解决方案 2:使用带有限定名称的惰性求值

根据qualified name的定义:

一个虚线名称,显示从一个模块的全局范围到一个 该模块中定义的类、函数或方法,如 PEP 中所定义 3155.对于顶级函数和类,限定名与对象名相同:

>>> class C:
...     class D:
...         def meth(self):
...             pass
...
>>> C.__qualname__
'C'
>>> C.D.__qualname__
'C.D'
>>> C.D.meth.__qualname__
'C.D.meth'

示例代码:

from queue import Queue


class LazyInit:
    def __init__(self, name):
        self.name = name
    def __get__(self, instance, owner):
        return globals()[self.name]


class A:
    cls = LazyInit(locals()['__qualname__'])
    data_queue = None
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        cls = self.cls
        if cls.data_queue is None:
            cls.data_queue = Queue()


class B(A):
    pass

class C(A):
    pass


b = B()
c = C()

b.data_queue.put(5)
c.data_queue.get()  # Works fine

这两种解决方案都适合我,如果有其他更好的解决方案,请告诉我。 :)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-02-18
    • 1970-01-01
    • 2018-08-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-22
    • 2016-12-14
    相关资源
    最近更新 更多