【问题标题】:Python - Override parent class attribute without instantiationPython - 覆盖父类属性而不实例化
【发布时间】:2020-09-26 01:55:53
【问题描述】:

如何在子类中重写父类属性不使用任一类的对象实例?来自 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)(并且避免在每个派生类中复制/粘贴相同的属性/计算)。

如果不构造对象(我希望避免这种情况),我无法弄清楚如何实现这一点。有人有什么建议吗?提前致谢!

【问题讨论】:

  • Python 类也是对象。你可以这样对待它们。

标签: python oop inheritance overriding subclass


【解决方案1】:

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"

【讨论】:

  • @Mad_Physicist 非常感谢您的详细回答,这正是我想要的!我不知道元类或 init_subclass 的存在,我现在要把它们添加到我的工具带中!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-09-19
  • 1970-01-01
  • 2022-06-15
  • 1970-01-01
  • 1970-01-01
  • 2020-01-25
  • 2019-11-12
相关资源
最近更新 更多