这是如何从类定义向元类传递参数的python 3.x版本?问题,根据请求单独列出,因为答案与python 2.x明显不同。

在python 3.x中,如何将参数传递给元类的__prepare__、__new__和__init__函数,以便类作者可以向元类提供有关如何创建类的输入?

作为我的用例,我使用元类来实现类及其子类自动注册到pyyaml中,以便加载/保存yaml文件。这涉及到Pyyaml的股票YAMLObjectMetaClass中没有的一些额外的运行时逻辑。此外,我希望允许类作者可选地指定pyyaml用于表示类和/或用于构造和表示的函数对象的标记/标记格式模板。我已经知道我不能用pyyaml的YAMLObjectMetaClass的子类来完成这个任务,因为根据我的代码注释,我们没有访问__new__中的实际类对象的权限,所以我正在编写自己的元类来包装pyyaml的注册函数。

最后,我想做的事情是:

from myutil import MyYAMLObjectMetaClass

class MyClass(metaclass=MyYAMLObjectMetaClass):

__metaclassArgs__ = ()

__metaclassKargs__ = {"tag":"!MyClass"}

…当MyClass类对象被创建时,__metaclassArgs__和__metaclassKargs__将成为__prepare__、__new__和__init__方法的论据。

当然,我可以使用这个问题的python 2.x版本中列出的"保留属性名"方法,但我知道有一种更优雅的方法可用。

在深入研究了python的官方文档之后,我发现python 3.x提供了一种向元类传递参数的本地方法,尽管它并非没有缺陷。

只需在类声明中添加其他关键字参数:

class C(metaclass=MyMetaClass, myArg1=1, myArg2=2):

pass

…它们会像这样传递到您的元类中:

class MyMetaClass(type):

@classmethod

def __prepare__(metacls, name, bases, **kargs):

#kargs = {"myArg1": 1,"myArg2": 2}

return super().__prepare__(name, bases, **kargs)

def __new__(metacls, name, bases, namespace, **kargs):

#kargs = {"myArg1": 1,"myArg2": 2}

return super().__new__(metacls, name, bases, namespace)

#DO NOT send"**kargs" to"type.__new__".  It won't catch them and

#you'll get a"TypeError: type() takes 1 or 3 arguments" exception.

def __init__(cls, name, bases, namespace, myArg1=7, **kargs):

#myArg1 = 1  #Included as an example of capturing metaclass args as positional args.

#kargs = {"myArg2": 2}

super().__init__(name, bases, namespace)

#DO NOT send"**kargs" to"type.__init__" in Python 3.5 and older.  You'll get a

#"TypeError: type.__init__() takes no keyword arguments" exception.

您必须让kargs退出对type.__new__和type.__init__的调用(python 3.5及更高版本;请参阅下面的"更新"),否则由于传递的参数太多,将获得TypeError异常。这意味着,当以这种方式传递元类参数时,我们总是必须实现MyMetaClass.__new__和MyMetaClass.__init__,以防止自定义关键字参数到达基类type.__new__和type.__init__方法。type.__prepare__似乎能很好地处理额外的关键字参数(因此,在本例中,我为什么要传递这些参数,只是为了防止我不知道的某些功能依赖于**kargs,所以定义type.__prepare__是可选的。更新

在python 3.6中,似乎已经调整了type,type.__init__现在可以优雅地处理额外的关键字参数。您仍然需要定义type.__new__(抛出TypeError: __init_subclass__() takes no keyword arguments异常)。击穿

在Python3中,通过关键字参数而不是类属性指定元类:

class MyClass(metaclass=MyMetaClass):

pass

这句话大致翻译为:

MyClass = metaclass(name, bases, **kargs)

…其中metaclass是您传入的"元类"参数的值,name是您的类的字符串名称('MyClass',bases是您传入的任何基类(本例中为零长度的tuple ()),kargs是任何未捕获的关键字参数(本例中为空的dict{})。案例)。

进一步细分,该声明大致翻译为:

namespace = metaclass.__prepare__(name, bases, **kargs)  #`metaclass` passed implicitly since it's a class method.

MyClass = metaclass.__new__(metaclass, name, bases, namespace, **kargs)

metaclass.__init__(MyClass, name, bases, namespace, **kargs)

…其中kargs始终是未捕获关键字参数的dict,我们将其传递给类定义。

分解我上面给出的示例:

class C(metaclass=MyMetaClass, myArg1=1, myArg2=2):

pass

…大致翻译为:

namespace = MyMetaClass.__prepare__('C', (), myArg1=1, myArg2=2)

#namespace={'__module__': '__main__', '__qualname__': 'C'}

C = MyMetaClass.__new__(MyMetaClass, 'C', (), namespace, myArg1=1, myArg2=2)

MyMetaClass.__init__(C, 'C', (), namespace, myArg1=1, myArg2=2)

大部分信息来自于Python关于"定制类创建"的文档。

>在python 3中,通过关键字参数而不是class属性指定元类:此样式与python2不向后兼容,请参见stackoverflow.com/a/32713241/3285282

谢谢你的编辑,@drhagen.这提醒了我为什么要写神秘的代码注释"因为我们无法访问__new__中的实际类对象"。我对这个问题的python2版本的原始答案也进行了更新。

下面是我对另一个关于元类参数的问题的答案中的一个版本的代码,这个问题已经被更新,以便它在Python2和3中都能工作。它本质上与Benjamin Peterson的六个模块的with_metaclass()函数所做的相同——即在需要时使用所需的元类显式地创建一个新的基类,从而避免由于两个版本的python之间的元类语法差异而导致的错误(因为这样做的方法不会改变的。

from __future__ import print_function

from pprint import pprint

class MyMetaClass(type):

def __new__(cls, class_name, parents, attrs):

if 'meta_args' in attrs:

meta_args = attrs['meta_args']

attrs['args'] = meta_args[0]

attrs['to'] = meta_args[1]

attrs['eggs'] = meta_args[2]

del attrs['meta_args'] # clean up

return type.__new__(cls, class_name, parents, attrs)

# Creates base class on-the-fly using syntax which is valid in both

# Python 2 and 3.

class MyClass(MyMetaClass("NewBaseClass", (object,), {})):

meta_args = ['spam', 'and', 'eggs']

myobject = MyClass()

pprint(vars(MyClass))

print(myobject.args, myobject.to, myobject.eggs)

输出:

dict_proxy({'to': 'and', '__module__': '__main__', 'args': 'spam',

'eggs': 'eggs', '__doc__': None})

spam and eggs

下面是在python 3中向元类传递参数的最简单方法:Python 3 x

class MyMetaclass(type):

def __new__(mcs, name, bases, namespace, **kwargs):

return super().__new__(mcs, name, bases, namespace)

def __init__(cls, name, bases, namespace, custom_arg='default'):

super().__init__(name, bases, namespace)

print('Argument is:', custom_arg)

class ExampleClass(metaclass=MyMetaclass, custom_arg='something'):

pass

您还可以为只使用__init__和其他参数的元类创建一个基类:

class ArgMetaclass(type):

def __new__(mcs, name, bases, namespace, **kwargs):

return super().__new__(mcs, name, bases, namespace)

class MyMetaclass(ArgMetaclass):

def __init__(cls, name, bases, namespace, custom_arg='default'):

super().__init__(name, bases, namespace)

print('Argument:', custom_arg)

class ExampleClass(metaclass=MyMetaclass, custom_arg='something'):

pass

In Python 3, you specify a metaclass via keyword argument rather than class attribute:

值得一提的是,这种样式与Python2不向后兼容。如果要同时支持python 2和3,则应使用:

from six import with_metaclass

# or

from future.utils import with_metaclass

class Form(with_metaclass(MyMetaClass, object)):

pass

感谢您的分享,但这个答案只演示了如何以交叉兼容的方式传递元类。如何用这种方法处理元类的参数?

python类方法需要传入cls参数_如何从Python 3.x中的类定义传递参数到元类?相关推荐

  1. python语法中infile语句_浅谈pymysql查询语句中带有in时传递参数的问题

    直接给出例子说明: cs = conn.cursor() img_ids = [1,2,3] sql = "select img_url from img_url_table where i ...

  2. python查询数据库带逗号_浅谈pymysql查询语句中带有in时传递参数的问题

    直接给出例子说明: cs = conn.cursor() img_ids = [1,2,3] sql = "select img_url from img_url_table where i ...

  3. 廖雪峰讲python高阶函数求导_廖雪峰python课程笔记

    1. 可以通过 print(os.sys.path) 来查看python可导入的包的路径情况,这会打印出一个list.当我们import某一个包时,python会根据这个list,从前向后搜寻相匹配的 ...

  4. python 反向传值_Django中reverse反转并且传递参数的方法

    在写项目的过程中,有些函数不可避免的需要传入参数进去,所以我们在使用reverse进行反转时也需要传递参数.这个时候我们就可以使用 'reverse()' 中的 kwargs 参数了,它将传入一个字典 ...

  5. python的名字来源于什么故事_从认识Python开始吧

    认识Python 编写Python程序的文件,称为Python的脚本或者程序 要求Python文件的后缀名为.py print()是一个打印函数,可以输出数字.字符串.或者其他的内容 函数就是为了实现 ...

  6. java main 参数传递参数_Java千问:Java语言如何给main方法传递参数?

    大家都知道,Java语言运行程序的入口方法叫做main,这个方法有一个参数,这就表示说,我们要运行主方法,就必须给main方法传递一个参数.但是,大家似乎对这个参数向来都是视而不见,从来都没给主方法传 ...

  7. python 函数不确定传入参数_如何使用python传入不确定个数参数

    如何使用python传入不确定个数参数 这篇文章主要介绍了如何使用python传入不确定个数参数,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Py ...

  8. python中什么是关键字参数_如何使用python语言中函数的关键字参数的用法

    一般情况下,在调用函数时,使用的是位置参数,即是按照参数的位置来传值:关键字参数是按照定义函数传入的参数名称来传值的.那么,关键字参数怎么使用? 工具/原料 python pycharm 截图工具 W ...

  9. python函数设置默认参数_深入讲解Python函数中参数的使用及默认参数的陷阱

    这篇文章主要介绍了Python函数中参数的使用及默认参数的陷阱,文中将函数的参数分为必选参数.默认参数.可变参数和关键字参数来讲,要的朋友可以参考下 C++里函数可以设置缺省参数,Java不可以,只能 ...

最新文章

  1. 程序员感叹一年只能存下15万太少了……网友:潸然泪下
  2. 联邦学习:AI大规模落地又一革命性突破
  3. 再探@font-face及webIcon制作
  4. 重构-改善既有代码的设计:编写代码22宗罪(三)
  5. django html5 关系,Django REST FrameWork中文教程5:关系和超链接API
  6. C++ STL中vector用法简要总结
  7. (转)IIS7 配置ASP.NET 2.0, WCF, ASP.NET MVC
  8. 2013\National _C_C++_A\2.骰子迷题
  9. 基于物品的协同过滤推荐算法_《推荐系统实践》3.基于物品的协同过滤算法
  10. scp复制本地文件到远程服务器,scp 本地文件到远程服务器
  11. 32个机械动图,揭秘生活中制造原理
  12. 阻塞与非阻塞、同步与异步 I/O模型
  13. 一位程序猿面试蚂蚁金服后端的经验总结!
  14. CSS中常用的选择器
  15. 四、公文流转的基本过程
  16. 四大基本反应类型的关系_中学化学中的四种基本反应类型,氧化还原反应和物质的变化!...
  17. 腾讯AI Lab开放文本理解系统TexSmart,让AI想得更深更广
  18. 物联网协议-CoAP协议简介
  19. 离散数学知识点总结(2):命题公式的类型
  20. 软工作业 2:时事点评-红芯浏览器事件

热门文章

  1. [UVa 122] Trees On the Level
  2. 链表的常见操作(转)
  3. gzip: File too large错误
  4. python抓取网页图片
  5. POJ 3630 Phone List [Trie]
  6. php tp5.3,[李景山php]每天TP5-20161226|thinkphp5-Console.php-3
  7. python 日期处理_python 处理时间和日期
  8. 1000道Python题库系列分享25(40道Pandas客观题)
  9. 1900页Python系列PPT分享四:字符串与正则表达式(109页)
  10. Python编程常见出错信息及原因分析(4)