Python - 在不实例化的情况下覆盖父 class 属性

Python - Override parent class attribute without instantiation

如何在不使用 class 的对象实例的情况下覆盖子 class 中的父 class 属性? 来自 Java/C++ 的世界及其严格的结构设计,我发现自己受到 Python 做事方式的挑战。我想保持相对静止。

示例:

from urllib.parse import urljoin

class base:
    host = "/host/"
    path = "Override this in child classes"
    url = urljoin(host, path)

class config(base):
    path = "config"

    @classmethod
    def print_url(cls):
        print(cls.url) # Currently prints "/host/Override this in child classes"
                       # Would like to print "/host/config" instead

class log(base):
    path = "log"

    @classmethod
    def print_url(cls):
        print(cls.url) # Currently prints "/host/Override this in child classes"
                       # Would like to print "/host/log" instead

所需用法:

>>> config.print_url()
/host/config

>>> log.print_url()
/host/log

我希望 config.pathlog.path 属性覆盖 base.path。这样我就可以在 base class 中一劳永逸地使用 url = urljoin(host, path) (并避免在每个派生 [=] 中都必须 copy/paste 相同的 attribute/calculation 41=]).

如果不构造对象(我希望避免),我无法弄清楚如何完成此操作。有人有什么建议吗?提前致谢!

child path 属性会覆盖 base.path。您不覆盖的是 url 属性。当 base 的主体是 运行 创建 class object.

时计算一次

你有几个选择。无论哪种方式,您都需要使 url 动态计算,每次访问时,或者每个 child class.

至少一次

最简单的方法就是把url变成classmethod:

class base:
    host = "/host/"
    path = "Override this in child classes"

    @classmethod
    def url(cls):
        return urljoin(cls.host, cls.path)

    @classmethod
    def print_url(cls):
        print(cls.url())

class config(base):
    path = "config"

class log(base):
    path = "log"

请注意,您现在指的是实际的 classe 的 hostpath。您还只需要 base 中的一个 print_url 方法,而不是每个 class.

中的一个不同的方法

另一种选择是给 base,因此它的所有 children,元 class 与 url 作为 property:

class url_meta(type):
    @property
    def url(cls):
        return urljoin(cls.host, cls.path)

class base(metaclass=url_meta):
    host = "/host/"
    path = "Override this in child classes"

    @classmethod
    def print_url(cls):
        print(cls.url)

class config(base):
    path = "config"

class log(base):
    path = "log"

这是可行的,因为 python class 也是 object。您可以在 class 的 class 中定义 property(metaclass),它的行为与任何 property 对实例的行为相同.只是这一次实例本身就是一个class。

第三个选项是确保 url 在每个 child 中静态但正确地定义。 __init_subclass__ 方法允许您直接从 base:

非常方便地执行此操作
class base:
    host = "/host/"
    path = "Override this in child classes"
    url = urljoin(host, path)

    @classmethod
    def __init_subclass__(cls):
        cls.url = urljoin(cls.host, cls.path)

    @classmethod
    def print_url(cls):
        print(cls.url)

class config(base):
    path = "config"

class log(base):
    path = "log"

您也可以使用 metaclass 完成同样的事情:

class url_meta2(type):
    def __init__(cls, *args, **kwargs):
        cls.url = urljoin(cls.host, cls.path)

class base(metaclass=url_meta2):
    host = "/host/"
    path = "Override this in child classes"

    @classmethod
    def print_url(cls):
        print(cls.url)

class config(base):
    path = "config"

class log(base):
    path = "log"