如何将参数传递到 python 中的嵌套 class
how to pass arguments into a nested class in python
我有以下型号:
def get_my_model(name1, name2):
class MyModel(Model):
class Meta:
table1 = name1
table2 = name2
return MyModel
我试图在没有函数的情况下编写此代码,并想将参数 name1
和 name2
直接传递给 Meta class,但我不确定如何传递。我尝试使用构造函数,但不知何故不起作用。
class MyModel(Model):
def __init__(self, name1, name2):
self.name1 = name1
self.name2 = name2
class Meta:
table1 = self.name1
table2 = self.name2
我也试过在 meta class 上设置构造函数,但也没有用。有人有想法吗?
TL;DR:
- 来自
MyModel
的任何可访问变量或来自名为 Meta
的 class 的任何更高范围的变量,即 已经 定义
- 修改
ModelBase
的metaclass(修补文件或继承自)
这不是元数据class!它只是另一个 class 范围内名为 Meta
的 class。发生的事情是 Python 有单独的 contexts/scopes 一旦执行环境开始构建:
- interpreter
- global(启动解释器后给定的原始命名空间,
globals()
)
然后是全局命名空间中的嵌套:
- module/file
- class
- 函数
- 也许还有其他一些
您不能将参数传递给 Meta
class,因为它只是在其中声明。它没有被调用。与此并行的是将参数传递给模块范围内的 class 声明:
# module.py
class MyClass:
value = <how?>
一旦找到调用的地方,然后你可以通过修改调用函数来注入参数
class Main:
class Meta:
def __init__(self, *args, **kwargs):
print("Meta", args, kwargs)
def __init__(self, name):
Main.Meta(name)
print(Main(123))
如果我没有显式调用Main.Meta()
,本例中的Meta
class将不会被实例化。
对于 Django,Meta
class 是通过 getattr()
for the model class here, therefore you need to target ModelBase.__new__()
with super()
/copy-paste to modify the function so it accepts custom arguments, or, pass them as just class variables (how it's mostly done in Django / DRF) 拉取的。
class Main:
class Meta:
class_var = 123
从 Django 中的 implementation of Model
class 判断,您可能可以交换 metaclass
值,但我不确定作为元[=] 的 __new__()
的继承134=] 在声明使用它的 class 时执行:
# the original ModelBase from class Model(metaclass=ModelBase)
class Meta(type):
def __new__(cls, *_, **__):
print("Hello")
return cls
class MyMeta(Meta):
def __new__(cls, *_, **__):
print("Hi")
# implement your stuff here or copy-paste + patch
return cls
class Model(metaclass=Meta):
pass
class CustomModel(metaclass=MyMeta):
pass
class CustomModelWithInheritance(Model, metaclass=MyMeta):
pass
对于元classes 检查:
- this question
- this essay
- docs
- its tests
关于 self
:命名本身并不重要,也不会在您使用它的地方起作用,因为 self
只是一个 class 的隐式传递实例方法(引用 class 实例的函数):
class MyClass:
def func(self_or_other_name):
print(self_or_other_name)
MyClass().func()
当在 metaclass 中创建 class 时,cls
参数在 __new__()
方法中的行为方式相同,即它是对 [=84= 的引用]metaclass 实例(名称空间中的 class 声明),其“描述”是 metaclass创建它。
cls = type("MyClass", (), {}) # create an instance of "type"
cls # <class '__main__.MyClass'>
# "cls" is class and an instance at the same time
唯一可以用来引用 class 范围的“特殊”变量是 locals()
+ 在 class 范围内定义为变量的任何内容 + 在更高范围内的任何内容来自嵌套的 class、模块或其他模块:
编辑:
所以对于 class 变量它是一个搞砸了,这是正确的解释。对于您的情况,它将是一个函数或 metaclass 补丁,因为我们在这里处理寻找“先有鸡还是先有蛋”问题的开始。我们可以通过从上方查看 - 在上层 class 定义完成之前引入函数范围和设置值 - 或从创建过程内部查看(元 class)。
我们不能使用 class 变量,因为我们仍在定义 class 的过程中。我们也不能使用__init__
引用class(或Meta
嵌套class)在那里注入一个值,因为我们已经处于 metaclass' __new__()
方法已经执行的情况,因此您在那里设置的任何内容都可能会在您的情况下使用,但在创建 MyModel
class.
class MyModel(Model):
def __init__(self, name):
MyModel.name = name # MyModel not defined yet
# but we can use self.__class__ in the post-poned execution
# once MyModel is defined (case when calling __init__())
# we haven't even started to define the Meta nested class
# starting to define Meta nested class
class Meta:
# MyModel not defined yet because the code block
# of MyModel class isn't finished yet
table = MyModel.name
# can't reference because it doesn't exist yet,
# same applies for Meta, the only thing you have now is
# the __qualname__ string
但是!
from pprint import pprint
class RealMetaClass(type):
def __new__(cls, *_, **__):
# create a new class - "Main" because it uses "metaclass" kwarg
new_class = super().__new__(cls, *_, **__)
# once we have the "Main" class, we can reference it the same way
# like *after* we define it the normal way
nested_meta = getattr(new_class, "NestedClassNamedMeta")
# ~= pprint(getattr(Main, "NestedClassNamedMeta"))
pprint(("in metaclass", nested_meta))
pprint(("in metaclass", dir(nested_meta)))
pprint(("in metaclass", vars(nested_meta)))
return new_class
class CustomMetaClass(RealMetaClass):
def __new__(cls, *_, **__):
new_class = super().__new__(cls, *_, **__)
nested_meta = getattr(new_class, "NestedClassNamedMeta")
# set a new class variable without affecting previous __new__() call
new_class.custom_thing = getattr(nested_meta, "custom_thing", None)
# do my stuff with custom attribute, that's not handled by Django
return new_class
class Main(metaclass=RealMetaClass):
pprint("defining Main class")
def __init__(self, name):
pprint(("in instance", self.__class__.NestedClassNamedMeta.name))
# works, because it's called after the Main class block
# is finished and executed i.e. defined
self.__class__.NestedClassNamedMeta.new_value = "hello"
class NestedClassNamedMeta:
name = "John"
custom_thing = "custom"
class CustomMain(Main, metaclass=CustomMetaClass):
class NestedClassNamedMeta:
name = "John"
custom_thing = "custom"
instance = Main("Fred")
# custom_thing is ignored, not added to instance.__class__
pprint(("after init", vars(instance.__class__), vars(instance.NestedClassNamedMeta)))
instance = CustomMain("Fred")
# custom_thing is processed, added to instance.__class__
pprint(("after init", vars(instance.__class__), vars(instance.NestedClassNamedMeta)))
我有以下型号:
def get_my_model(name1, name2):
class MyModel(Model):
class Meta:
table1 = name1
table2 = name2
return MyModel
我试图在没有函数的情况下编写此代码,并想将参数 name1
和 name2
直接传递给 Meta class,但我不确定如何传递。我尝试使用构造函数,但不知何故不起作用。
class MyModel(Model):
def __init__(self, name1, name2):
self.name1 = name1
self.name2 = name2
class Meta:
table1 = self.name1
table2 = self.name2
我也试过在 meta class 上设置构造函数,但也没有用。有人有想法吗?
TL;DR:
- 来自
MyModel
的任何可访问变量或来自名为Meta
的 class 的任何更高范围的变量,即 已经 定义 - 修改
ModelBase
的metaclass(修补文件或继承自)
这不是元数据class!它只是另一个 class 范围内名为 Meta
的 class。发生的事情是 Python 有单独的 contexts/scopes 一旦执行环境开始构建:
- interpreter
- global(启动解释器后给定的原始命名空间,
globals()
)
然后是全局命名空间中的嵌套:
- module/file
- class
- 函数
- 也许还有其他一些
您不能将参数传递给 Meta
class,因为它只是在其中声明。它没有被调用。与此并行的是将参数传递给模块范围内的 class 声明:
# module.py
class MyClass:
value = <how?>
一旦找到调用的地方,然后你可以通过修改调用函数来注入参数
class Main:
class Meta:
def __init__(self, *args, **kwargs):
print("Meta", args, kwargs)
def __init__(self, name):
Main.Meta(name)
print(Main(123))
如果我没有显式调用Main.Meta()
,本例中的Meta
class将不会被实例化。
对于 Django,Meta
class 是通过 getattr()
for the model class here, therefore you need to target ModelBase.__new__()
with super()
/copy-paste to modify the function so it accepts custom arguments, or, pass them as just class variables (how it's mostly done in Django / DRF) 拉取的。
class Main:
class Meta:
class_var = 123
从 Django 中的 implementation of Model
class 判断,您可能可以交换 metaclass
值,但我不确定作为元[=] 的 __new__()
的继承134=] 在声明使用它的 class 时执行:
# the original ModelBase from class Model(metaclass=ModelBase)
class Meta(type):
def __new__(cls, *_, **__):
print("Hello")
return cls
class MyMeta(Meta):
def __new__(cls, *_, **__):
print("Hi")
# implement your stuff here or copy-paste + patch
return cls
class Model(metaclass=Meta):
pass
class CustomModel(metaclass=MyMeta):
pass
class CustomModelWithInheritance(Model, metaclass=MyMeta):
pass
对于元classes 检查:
- this question
- this essay
- docs
- its tests
关于 self
:命名本身并不重要,也不会在您使用它的地方起作用,因为 self
只是一个 class 的隐式传递实例方法(引用 class 实例的函数):
class MyClass:
def func(self_or_other_name):
print(self_or_other_name)
MyClass().func()
当在 metaclass 中创建 class 时,cls
参数在 __new__()
方法中的行为方式相同,即它是对 [=84= 的引用]metaclass 实例(名称空间中的 class 声明),其“描述”是 metaclass创建它。
cls = type("MyClass", (), {}) # create an instance of "type"
cls # <class '__main__.MyClass'>
# "cls" is class and an instance at the same time
唯一可以用来引用 class 范围的“特殊”变量是 locals()
+ 在 class 范围内定义为变量的任何内容 + 在更高范围内的任何内容来自嵌套的 class、模块或其他模块:
编辑:
所以对于 class 变量它是一个搞砸了,这是正确的解释。对于您的情况,它将是一个函数或 metaclass 补丁,因为我们在这里处理寻找“先有鸡还是先有蛋”问题的开始。我们可以通过从上方查看 - 在上层 class 定义完成之前引入函数范围和设置值 - 或从创建过程内部查看(元 class)。
我们不能使用 class 变量,因为我们仍在定义 class 的过程中。我们也不能使用__init__
引用class(或Meta
嵌套class)在那里注入一个值,因为我们已经处于 metaclass' __new__()
方法已经执行的情况,因此您在那里设置的任何内容都可能会在您的情况下使用,但在创建 MyModel
class.
class MyModel(Model):
def __init__(self, name):
MyModel.name = name # MyModel not defined yet
# but we can use self.__class__ in the post-poned execution
# once MyModel is defined (case when calling __init__())
# we haven't even started to define the Meta nested class
# starting to define Meta nested class
class Meta:
# MyModel not defined yet because the code block
# of MyModel class isn't finished yet
table = MyModel.name
# can't reference because it doesn't exist yet,
# same applies for Meta, the only thing you have now is
# the __qualname__ string
但是!
from pprint import pprint
class RealMetaClass(type):
def __new__(cls, *_, **__):
# create a new class - "Main" because it uses "metaclass" kwarg
new_class = super().__new__(cls, *_, **__)
# once we have the "Main" class, we can reference it the same way
# like *after* we define it the normal way
nested_meta = getattr(new_class, "NestedClassNamedMeta")
# ~= pprint(getattr(Main, "NestedClassNamedMeta"))
pprint(("in metaclass", nested_meta))
pprint(("in metaclass", dir(nested_meta)))
pprint(("in metaclass", vars(nested_meta)))
return new_class
class CustomMetaClass(RealMetaClass):
def __new__(cls, *_, **__):
new_class = super().__new__(cls, *_, **__)
nested_meta = getattr(new_class, "NestedClassNamedMeta")
# set a new class variable without affecting previous __new__() call
new_class.custom_thing = getattr(nested_meta, "custom_thing", None)
# do my stuff with custom attribute, that's not handled by Django
return new_class
class Main(metaclass=RealMetaClass):
pprint("defining Main class")
def __init__(self, name):
pprint(("in instance", self.__class__.NestedClassNamedMeta.name))
# works, because it's called after the Main class block
# is finished and executed i.e. defined
self.__class__.NestedClassNamedMeta.new_value = "hello"
class NestedClassNamedMeta:
name = "John"
custom_thing = "custom"
class CustomMain(Main, metaclass=CustomMetaClass):
class NestedClassNamedMeta:
name = "John"
custom_thing = "custom"
instance = Main("Fred")
# custom_thing is ignored, not added to instance.__class__
pprint(("after init", vars(instance.__class__), vars(instance.NestedClassNamedMeta)))
instance = CustomMain("Fred")
# custom_thing is processed, added to instance.__class__
pprint(("after init", vars(instance.__class__), vars(instance.NestedClassNamedMeta)))