python - 如何将参数传递给python中的嵌套类
问题描述
我有以下模型:
def get_my_model(name1, name2):
class MyModel(Model):
class Meta:
table1 = name1
table2 = name2
return MyModel
我试图在没有函数的情况下编写它,并希望将参数name1
直接name2
传递给 Meta 类,但我不确定如何。我尝试使用构造函数,但不知何故不起作用。
class MyModel(Model):
def __init__(self, name1, name2):
self.name1 = name1
self.name2 = name2
class Meta:
table1 = self.name1
table2 = self.name2
我还尝试在元类上设置构造函数,但也没有用。任何人的想法?
解决方案
TL;博士:
- 已定义的类命名的任何可访问变量
MyModel
或任何更高范围的变量Meta
- modify
ModelBase
的元类(修补文件或继承自)
它不是元类!它只是其他类范围内的一个类,名为Meta
. 发生的事情是,一旦执行环境开始构建,Python 就会有单独的上下文/范围:
然后是全局命名空间中的嵌套:
- 模块/文件
- 班级
- 功能
- 也许其他一些
您不能将参数传递给Meta
类,因为它只是在那里声明的。它没有被调用。与此并行的是将参数从模块范围传递给类声明:
# 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
则不会实例化此示例中的类。
在 Django 的情况下,模型类的类是通过hereMeta
提取的,因此您需要使用/copy-paste 来修改函数,以便它接受自定义参数,或者将它们作为类变量传递(它主要是如何完成的Django / DRF)。getattr()
ModelBase.__new__()
super()
class Main:
class Meta:
class_var = 123
从Django 中类的实现Model
来看,您可能可以交换metaclass
值,但我不确定继承,因为在__new__()
声明使用它的类时会执行元类:
# 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
对于元类检查:
关于self
:命名本身无关紧要,也不会在您使用它的地方起作用,因为它self
只是将类的实例隐式传递给方法(引用类实例的函数):
class MyClass:
def func(self_or_other_name):
print(self_or_other_name)
MyClass().func()
在元类中创建类时,方法中的cls
参数的行为方式相同,即它是对元类实例的引用(命名空间中的类声明),“描述”是创建它的元类。__new__()
cls = type("MyClass", (), {}) # create an instance of "type"
cls # <class '__main__.MyClass'>
# "cls" is class and an instance at the same time
您可以用来引用类范围的唯一“特殊”变量是locals()
+在类范围内定义为变量的任何内容+更高范围内的任何内容,无论是来自嵌套类、模块还是其他:
编辑:
所以对于类变量,这是一个错误,这是正确的解释。对于您的情况,它可能是一个函数或元类修补,因为我们在这里处理的是寻找“鸡和蛋”问题的开始。我们可以通过从上方查看 - 在上层类的定义完成之前引入函数范围和设置值 - 或从创建过程(元类)内部查看。
我们不能使用类变量,因为我们仍在定义类的过程中。我们也不能使用__init__
引用类(或Meta
嵌套类)来在那里注入一个值,因为我们已经处于元类__new__()
方法已经执行的情况,因此您在那里设置的任何内容都可能在您的案例,但在创建MyModel
类时不会出现。
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)))
推荐阅读
- foreach - 将文件夹复制到 Azure 数据存储(azure 数据工厂)
- arrays - 格式化一个对象来拆分数据的头部
- restructuredtext - 狮身人面像呈现 :ref: 斜体 - 我怎样才能删除它?
- python-3.x - 字典列表中的动态值python
- c++ - 在 Windows 上以编程方式关闭弹出菜单
- python - AttributeError:模块 'socket' 没有属性 'setblocking'
- javascript - 如何在类组件中以正确的方式使用 setState 更新数组内对象的属性?
- c# - 如何从xml文件中获取嵌套元素
- ruby-on-rails - ckeditor4:图片上传功能设置默认宽高
- node.js - 如何使用 sequelize/sequelize 将表值参数传递给 mssql 中的存储过程?