Как мне переопределить атрибут родительского класса в дочернем классе без использования экземпляров объектов любого класса? Исходя из мира 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.path
и log.path
переопределяли base.path
. Так я могу использовать url = urljoin(host, path)
раз и навсегда в классе base
(и избежать необходимости копировать / вставлять тот же атрибут / вычисление в каждый производный класс).
Я не могу понять, как этого добиться без создания объектов (чего я надеюсь избежать). У кого-нибудь есть совет? Заранее спасибо!
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"
Обратите внимание, что вы сейчас имеете в виду актуальные классы host
и path
на лету. Вам также понадобится только один метод print_url
в base
вместо разных в каждом классе.
Другой вариант - предоставить 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"
Похожие вопросы
Новые вопросы
python
Python — это мультипарадигмальный многоцелевой язык программирования с динамической типизацией. Он предназначен для быстрого изучения, понимания и использования, а также обеспечивает чистый и унифицированный синтаксис. Обратите внимание, что Python 2 официально не поддерживается с 01.01.2020. Если у вас есть вопросы о версии Python, добавьте тег [python-2.7] или [python-3.x]. При использовании варианта Python (например, Jython, PyPy) или библиотеки (например, Pandas, NumPy) укажите это в тегах.