在 Python 中根据上下文注入静态 class 属性
Contextually injecting static class properties in Python
我正在编写一个网站生成器,其中包含各种 class 元素,这些元素表示网页中的内容,例如 Page
、NewsPost
、Tag
、[=16] =]等
我希望能够简单地构造这些对象,对此我没有问题。
但是,我还想在特定上下文中构建这些对象 - 例如,具有特定根 URL 的网站的上下文。假设我将此上下文放入 class ContentManager
的实例中。这是我最终希望得到的代码:
page = Page(title='Test Page', content='hello world!')
assert page.cm == None
cm = ContentManager(root_url='//localhost')
page = cm.Page(title='Test Page', content='hello world!')
assert page.cm == cm
如果 page.cm
是在 __init__
中设置的每个实例 属性,我可以轻松地管理它,但我需要在 cm.Page
上调用 class 方法] 需要访问 cm
对象,因此它必须是静态的 属性.
如果我只是在 Page
class 上将其设置为静态 属性,它最终会影响 other ContentManager
s 页面也是如此,这是不可取的。
我将如何实现这一目标?元classes?或者某种 class 工厂函数?
抛开其他一切,您只需要动态构造一个 class 来绑定到 ContentManager
的每个实例;我们可以使用内置的 type
函数来做到这一点,该函数可以使用一个参数为我们提供对象的类型,或者使用三个参数 (class name, base classes,和 class 字典)构造一个新的 class.
以下是您所处情况的示例:
class Page(object):
# This is just a default value if we construct a Page
# outside the context of a ContentManager
cm = None
def __init__(self, *args, **kwargs):
self.args = args
self.kwargs = kwargs
@classmethod
def do_class_thing(cls):
return cls.cm
class ContentManager(object):
def __init__(self, root_url):
self.url = root_url
"""
This is where the magic happens. We're telling type() to
construct a class, with the class name ContentManagerPage,
have it inherit from the above explicitly-declared Page
class, and then overriding its __dict__ such that the class
cm variable is set to be the ContentManager we're
constructing it from.
"""
self.Page = type(str('ContentManagerPage'), (Page,), {'cm': self})
一旦你完成了所有这些设置,就可以很简单地做你想做的事情,用 cm 作为 class 变量。
一个解决方案可能是为每个 ContentManage
实例创建一个 Page
的子class:
class Page:
cm = None
def __init__(self, title, content):
self.title = title
self.content = content
class ContentManager:
def __init__(self, root_url):
class PerContentManagerPage(Page):
cm = self
self.Page = PerContentManagerPage
page0 = Page(title='Test Page', content='hello world!')
cm = ContentManager(root_url='//localhost')
page = cm.Page(title='Test Page', content='hello world!')
cm2 = ContentManager(root_url='//localhost')
page2 = cm2.Page(title='Test Page 2', content='hello world!')
assert page0.cm is None
assert page.cm == cm
assert page2.cm == cm2
在python中,一个class也是一个对象(它的元class的一个实例)。每次实例化 ContentManager
时,此解决方案都会创建 Page
的新子 class。这意味着 cm.Page
class 与 cm2.Page
class 不同,但两者都是 Page
的子 class。这就是为什么 cm.Page.cm
和 cm2.Page.cm
可能有不同的值,因为它们是两个独立的 class 对象(或 class 对象)。
注意:虽然在 python 中这可以通过动态创建子 class 对象来解决,但问题通常有更好的解决方案。动态创建 classes/subclasses 是一个警告信号 (HACK)。
我仍然相信您不应该为每个内容管理器实例创建一个页面子class。相反,我会简单地使用全局 ContentManager
和 Page
classes 的实例,通过以适当的方式将它们与彼此的引用连接起来,并将数据和代码放入实例 attributes/methods.
我正在编写一个网站生成器,其中包含各种 class 元素,这些元素表示网页中的内容,例如 Page
、NewsPost
、Tag
、[=16] =]等
我希望能够简单地构造这些对象,对此我没有问题。
但是,我还想在特定上下文中构建这些对象 - 例如,具有特定根 URL 的网站的上下文。假设我将此上下文放入 class ContentManager
的实例中。这是我最终希望得到的代码:
page = Page(title='Test Page', content='hello world!')
assert page.cm == None
cm = ContentManager(root_url='//localhost')
page = cm.Page(title='Test Page', content='hello world!')
assert page.cm == cm
如果 page.cm
是在 __init__
中设置的每个实例 属性,我可以轻松地管理它,但我需要在 cm.Page
上调用 class 方法] 需要访问 cm
对象,因此它必须是静态的 属性.
如果我只是在 Page
class 上将其设置为静态 属性,它最终会影响 other ContentManager
s 页面也是如此,这是不可取的。
我将如何实现这一目标?元classes?或者某种 class 工厂函数?
抛开其他一切,您只需要动态构造一个 class 来绑定到 ContentManager
的每个实例;我们可以使用内置的 type
函数来做到这一点,该函数可以使用一个参数为我们提供对象的类型,或者使用三个参数 (class name, base classes,和 class 字典)构造一个新的 class.
以下是您所处情况的示例:
class Page(object):
# This is just a default value if we construct a Page
# outside the context of a ContentManager
cm = None
def __init__(self, *args, **kwargs):
self.args = args
self.kwargs = kwargs
@classmethod
def do_class_thing(cls):
return cls.cm
class ContentManager(object):
def __init__(self, root_url):
self.url = root_url
"""
This is where the magic happens. We're telling type() to
construct a class, with the class name ContentManagerPage,
have it inherit from the above explicitly-declared Page
class, and then overriding its __dict__ such that the class
cm variable is set to be the ContentManager we're
constructing it from.
"""
self.Page = type(str('ContentManagerPage'), (Page,), {'cm': self})
一旦你完成了所有这些设置,就可以很简单地做你想做的事情,用 cm 作为 class 变量。
一个解决方案可能是为每个 ContentManage
实例创建一个 Page
的子class:
class Page:
cm = None
def __init__(self, title, content):
self.title = title
self.content = content
class ContentManager:
def __init__(self, root_url):
class PerContentManagerPage(Page):
cm = self
self.Page = PerContentManagerPage
page0 = Page(title='Test Page', content='hello world!')
cm = ContentManager(root_url='//localhost')
page = cm.Page(title='Test Page', content='hello world!')
cm2 = ContentManager(root_url='//localhost')
page2 = cm2.Page(title='Test Page 2', content='hello world!')
assert page0.cm is None
assert page.cm == cm
assert page2.cm == cm2
在python中,一个class也是一个对象(它的元class的一个实例)。每次实例化 ContentManager
时,此解决方案都会创建 Page
的新子 class。这意味着 cm.Page
class 与 cm2.Page
class 不同,但两者都是 Page
的子 class。这就是为什么 cm.Page.cm
和 cm2.Page.cm
可能有不同的值,因为它们是两个独立的 class 对象(或 class 对象)。
注意:虽然在 python 中这可以通过动态创建子 class 对象来解决,但问题通常有更好的解决方案。动态创建 classes/subclasses 是一个警告信号 (HACK)。
我仍然相信您不应该为每个内容管理器实例创建一个页面子class。相反,我会简单地使用全局 ContentManager
和 Page
classes 的实例,通过以适当的方式将它们与彼此的引用连接起来,并将数据和代码放入实例 attributes/methods.