繁体   English   中英

Python - 在没有实例化的情况下覆盖父类属性

[英]Python - Override parent class attribute without instantiation

如何在不使用任一类的对象实例的情况下覆盖子类中的父类属性 来自 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类中一劳永逸地使用url = urljoin(host, path) (并避免在每个派生类中复制/粘贴相同的属性/计算)。

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

path属性会覆盖base.path 您没有覆盖的是url属性。 当运行base的主体以创建类对象时,它会被计算一次。

您有几个选择。 无论哪种方式,您都需要动态计算url ,无论是每次访问它时,还是每个子类至少一次。

最简单的方法是将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"

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

另一种选择是为base及其所有子类提供一个以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 类也是对象。 您可以在类的类(元类)中定义一个property ,它的行为就像任何property对实例所做的一样。 只是这次实例本身是一个类。

第三个选项是确保url在每个孩子中都是静态但正确定义的。 __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"

你也可以用元类完成同样的事情:

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"

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM