Python之namedtuple源码分析建议收藏

namedtuple函数源码通过函数模板字符串_class_template.format()会生成我们需要的实例类:eg:people=namedtuple("person&qu

大家好,又见面了,我是全栈君,今天给大家准备了Idea注册码。

全栈程序员社区此处内容已经被作者隐藏,请输入验证码查看内容
验证码:
请关注本站微信公众号,回复“验证码”,获取验证码。在微信里搜索“全栈程序员社区”或者“www_javaforall_cn”或者微信扫描右侧二维码都可以关注本站微信公众号。

namedtuple()函数根据提供的参数创建一个新类,这个类会有一个类名,一些字段名和一个可选的用于定义类行为的关键字,具体实现如下

namedtuple函数源码

from keyword import iskeyword as _iskeyword
import sys as _sys


import logging
logging.basicConfig(level=logging.INFO, filename="logging.txt", filemode="w+", \
                    format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

_class_template = """\
from builtins import property as _property, tuple as _tuple
from operator import itemgetter as _itemgetter
from collections import OrderedDict

class {typename}(tuple):
    '{typename}({arg_list})'

    __slots__ = ()

    _fields = {field_names!r}

    def __new__(_cls, {arg_list}):
        'Create new instance of {typename}({arg_list})'
        return _tuple.__new__(_cls, ({arg_list}))

    @classmethod
    def _make(cls, iterable, new=tuple.__new__, len=len):
        'Make a new {typename} object from a sequence or iterable'
        result = new(cls, iterable)
        if len(result) != {num_fields:d}:
            raise TypeError('Expected {num_fields:d} arguments, got %d' % len(result))
        return result

    def _replace(_self, **kwds):
        'Return a new {typename} object replacing specified fields with new values'
        result = _self._make(map(kwds.pop, {field_names!r}, _self))
        if kwds:
            raise ValueError('Got unexpected field names: %r' % list(kwds))
        return result

    def __repr__(self):
        'Return a nicely formatted representation string'
        return self.__class__.__name__ + '({repr_fmt})' % self

    def _asdict(self):
        'Return a new OrderedDict which maps field names to their values.'
        return OrderedDict(zip(self._fields, self))

    def __getnewargs__(self):
        'Return self as a plain tuple.  Used by copy and pickle.'
        return tuple(self)

{field_defs}
"""

_repr_template = '{name}=%r'

_field_template = '''\
    {name} = _property(_itemgetter({index:d}), doc='Alias for field number {index:d}')
'''

def namedtuple(typename, field_names, *, verbose=False, rename=False, module=None):
    """Returns a new subclass of tuple with named fields.

    >>> Point = namedtuple('Point', ['x', 'y'])
    >>> Point.__doc__                   # docstring for the new class
    'Point(x, y)'
    >>> p = Point(11, y=22)             # instantiate with positional args or keywords
    >>> p[0] + p[1]                     # indexable like a plain tuple
    33
    >>> x, y = p                        # unpack like a regular tuple
    >>> x, y
    (11, 22)
    >>> p.x + p.y                       # fields also accessible by name
    33
    >>> d = p._asdict()                 # convert to a dictionary
    >>> d['x']
    11
    >>> Point(**d)                      # convert from a dictionary
    Point(x=11, y=22)
    >>> p._replace(x=100)               # _replace() is like str.replace() but targets named fields
    Point(x=100, y=22)

    """

    # Validate the field names.  At the user's option, either generate an error
    # message or automatically replace the field name with a valid name.
    if isinstance(field_names, str):
        field_names = field_names.replace(',', ' ').split()
    field_names = list(map(str, field_names))
    typename = str(typename)
    logging.info("%s:   %s" %(typename, field_names))
    if rename:
        seen = set()
        for index, name in enumerate(field_names):
            if (not name.isidentifier()
                or _iskeyword(name)
                or name.startswith('_')
                or name in seen):
                field_names[index] = '_%d' % index
            seen.add(name)
    for name in [typename] + field_names:
        logging.info(name)
        if type(name) is not str:
            raise TypeError('Type names and field names must be strings')
        #判断是否为标识符,标识符必须以字母或者“_”开头
        #标识符用于作为变量,函数名、类名、方法名等
        if not name.isidentifier(): 
            raise ValueError('Type names and field names must be valid '
                             'identifiers: %r' % name)
        #判断是否为关键字,关键字为python内部已经使用了的标识符                     
        if _iskeyword(name):
            raise ValueError('Type names and field names cannot be a '
                             'keyword: %r' % name)
    seen = set()
    for name in field_names:
        if name.startswith('_') and not rename:
            raise ValueError('Field names cannot start with an underscore: '
                             '%r' % name)
        if name in seen:
            raise ValueError('Encountered duplicate field name: %r' % name)
        seen.add(name)

    # Fill-in the class template
    class_definition = _class_template.format(
        typename = typename,
        field_names = tuple(field_names),
        num_fields = len(field_names),
        arg_list = repr(tuple(field_names)).replace("'", "")[1:-1],
        repr_fmt = ', '.join(_repr_template.format(name=name)
                             for name in field_names),
        field_defs = '\n'.join(_field_template.format(index=index, name=name)
                               for index, name in enumerate(field_names))                       
    )
    logging.info(class_definition)

    # Execute the template string in a temporary namespace and support
    # tracing utilities by setting a value for frame.f_globals['__name__']
    namespace = dict(__name__='namedtuple_%s' % typename)
    exec(class_definition, namespace)
    result = namespace[typename]
    result._source = class_definition
    if verbose:
        print(result._source)

    # For pickling to work, the __module__ variable needs to be set to the frame
    # where the named tuple is created.  Bypass this step in environments where
    # sys._getframe is not defined (Jython for example) or sys._getframe is not
    # defined for arguments greater than 0 (IronPython), or where the user has
    # specified a particular module.
    if module is None:
        try:
            module = _sys._getframe(1).f_globals.get('__name__', '__main__')
        except (AttributeError, ValueError):
            pass
    if module is not None:
        result.__module__ = module

    return result

  通过函数模板字符串_class_template.format()会生成我们需要的实例类:

  eg: people = namedtuple(“person”,”name,age,sex”) 

class person(tuple) 分析

#coding=utf-8

from builtins import property as _property, tuple as _tuple
from operator import itemgetter as _itemgetter
from collections import OrderedDict

import logging
logging.basicConfig(level=logging.INFO, filename="logging.txt", filemode="w+", \
                    format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

class person(tuple):
    'person(name, age, sex)'

    __slots__ = ()

    _fields = ('name', 'age', 'sex')

    def __new__(_cls, name, age, sex):
        'Create new instance of person(name, age, sex)'
        logger.info("__new__")
        return _tuple.__new__(_cls, (name, age, sex))

    @classmethod
    def _make(cls, iterable, new=tuple.__new__, len=len):
        'Make a new person object from a sequence or iterable'
        result = new(cls, iterable)
        if len(result) != 3:
            raise TypeError('Expected 3 arguments, got %d' % len(result))
        return result

    def _replace(_self, **kwds):
        'Return a new person object replacing specified fields with new values'

        #需要深刻理解该代码的精髓
        logger.info(type(_self))
        for item in _self:
            logger.info(item)
        str = '''
            how to replace dict_keyvalue
            li = map({"age":99}.pop, ('name', 'age', 'sex'), ("zhanglin", "11", "man"))
        '''
        logger.info(str)
        result = _self._make(map(kwds.pop, ('name', 'age', 'sex'), _self))
        if kwds:
            raise ValueError('Got unexpected field names: %r' % list(kwds))
        return result
        
    def __repr__(self):
        'Return a nicely formatted representation string'
        return self.__class__.__name__ + '(name=%r, age=%r, sex=%r)' % self

    def _asdict(self):
        'Return a new OrderedDict which maps field names to their values.'
        return OrderedDict(zip(self._fields, self)) #打包为元组列表

    def __getnewargs__(self):
        'Return self as a plain tuple.  Used by copy and pickle.'
        return tuple(self)

    name = _property(_itemgetter(0), doc='Alias for field number 0')

    age = _property(_itemgetter(1), doc='Alias for field number 1')

    sex = _property(_itemgetter(2), doc='Alias for field number 2')

if __name__ == "__main__":
 
    p1 = person("zhanglin", "30", "man")
    logger.info("{0}:{1}".format("p1", p1))
    #_replace验证   
    p2 = p1._replace(name ="zhangsan", age=99)
    logger.info(p2)
    logger.info("{0}:{1}".format("p2", p2))
    

测试结果:

2018-03-21 15:10:46,197 - __main__ - INFO - __new__
2018-03-21 15:10:46,197 - __main__ - INFO - p1:person(name='zhanglin', age='30', sex='man')
2018-03-21 15:10:46,197 - __main__ - INFO - <class '__main__.person'>
2018-03-21 15:10:46,197 - __main__ - INFO - zhanglin
2018-03-21 15:10:46,197 - __main__ - INFO - 30
2018-03-21 15:10:46,197 - __main__ - INFO - man
2018-03-21 15:10:46,197 - __main__ - INFO - 
            how to replace dict_keyvalue
            li = map({"age":99}.pop, ('name', 'age', 'sex'), ("zhanglin", "11", "man"))
        
2018-03-21 15:10:46,197 - __main__ - INFO - person(name='zhangsan', age=99, sex='man')
2018-03-21 15:10:46,197 - __main__ - INFO - p2:person(name='zhangsan', age=99, sex='man')

根据类的字符串模板创建类

#coding=utf-8

import logging

logging.basicConfig(level=logging.INFO, filename="logging.txt", filemode="w+",\
                    format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)                    

_class_template = \
'''
class {typename}(tuple):
    __slots__ = ()
    _fields = {field_names!r}
    def __new__(cls, *args, **dwargs):
        return super().__new__(cls, *args, **dwargs)
'''

def defineclass(typename, field_names):
    logging.info(field_names) 
    class_definition = _class_template.format(typename=typename,field_names=field_names,\
            #方法1
            arg_list = ','.join(name for name in field_names),\
            self_format = "".join("\t\tself.{name}={name}\n".format(name=name) for name in field_names))
            #方法2
            #arg_list = ', '.join('{name}'.format(name=name) for name in field_names))
             #方法3
            #arg_list = repr(tuple(field_names)).replace("'", "")[1:-1])
    logging.info(class_definition)

    namespace = dict(__name__='defineclass_%s' % typename)
    logging.info("namespce:{}".format(namespace))
    exec(class_definition, namespace)
    
    result = namespace[typename]
    logging.info("typename:{0}--result:{1}".format(typename, namespace[typename]))
    result._source = class_definition

    return result
        

if __name__ == "__main__":
 
    person = defineclass("person", ("name", "age", "sex")) 
    print (type(person))
    p = person(("zhanglin", "31", "man"))
    print (p)
    print (p[0])
2018-03-22 00:05:50,705 - root - INFO - ('name', 'age', 'sex')
2018-03-22 00:05:50,705 - root - INFO - 
class person(tuple):
    __slots__ = ()
    _fields = ('name', 'age', 'sex')
    def __new__(cls, *args, **dwargs):
        return super().__new__(cls, *args, **dwargs)

2018-03-22 00:05:50,705 - root - INFO - namespce:{'__name__': 'defineclass_person'}
2018-03-22 00:05:50,705 - root - INFO - typename:person--result:<class 'defineclass_person.person'>

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/120187.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • datagrip mac激活码【在线注册码/序列号/破解码】

    datagrip mac激活码【在线注册码/序列号/破解码】,https://javaforall.net/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

    2022年3月19日
    37
  • NMAKE编译CTK[通俗易懂]

    NMAKE编译CTK[通俗易懂]NMAKE编译CTK启动编译环境从VC中启动命令行或通过VC提供的批处理启动命令行,以能运行编译环境。如果装了多个VC版本,注意使用想要的VC版本启动安装编译环境。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5YlX5Gta-1597631378020)(study/image-20200815174602043.png)]再启动cmaked:\soft\ProgramFiles(x86)\MicrosoftVisualtudio\2017\Commun

    2022年6月5日
    51
  • 手机版mt4如何操作_安卓不能下载MT4

    手机版mt4如何操作_安卓不能下载MT4新手客户按照本文操作流程操作几遍,对MT4手机软件基本上就会非常熟练了。导航目录1.下载显示隐藏重新排序简单/高级视图新建图表行情周期添加/删除指标APP手机端MT4软件的功能模块一、软件下载登录1.下载通过无佣网开户的客户,下载对应APP手机版MT4软件后,手机桌面会有该交易商的软件图标,类似如下:2.登录点击MT4图标打开软件,点击软件左上角,进入“管理账户”里添加交易账户。已添加的交易账户,…

    2022年8月15日
    1
  • 什么是404页面,如何正确设置制作404页面

    什么是404页面,如何正确设置制作404页面
    什么是404页面?
      404网页是用户尝试访问网站不存在的网页(由于用户点击了损坏的链接、网页已被删除或用户输入了错误的网址)时看到的页面。之所以称为404网页,是因为针对丢失网页的请求,网络服务器会返回404HTTP状态代码,表明该网页未找到。
    404页面的目的是:告诉浏览者其所请求的页面不存在或链接错误,同时引导用户使用网站其他页面而不是关闭窗口离开。
    404对搜索引擎优化seo的影响
      搜索引擎通过HTTP状态码来识别网页的状态。当

    2022年7月27日
    11
  • lammps教程:薄膜渗透模拟(3)–不同孔隙率对过滤效果的影响

    lammps教程:薄膜渗透模拟(3)–不同孔隙率对过滤效果的影响本文是薄膜渗透过滤的最后一篇文章:不同孔隙率薄膜建模。孔隙或空位缺陷的建模原理比较简单:删除一定数量的原子就可以。lammps自带delete_atoms可以随机删除一定比例的原子,如果对孔隙或空位的形状、尺寸等有特殊需求,需要用编程的方法删除原子。delete_atomsporosity命令可随时产生设定比例的原子,如删除50%的原子:delete_atomsporositymembrane0.5482793membrane为原子组0.5为删除原子的比例482793为随机数种子

    2022年9月3日
    1
  • ReverseFind的用法 ; 查找字符中最后一个字符

    ReverseFind的用法 ; 查找字符中最后一个字符ReverseFindCString::ReverseFind  ReverseFind在一个较大的字符串中从末端开始查找某个字符  CString::ReverseFind  intReverseFind(TCHARch)const;  返回值:  返回此CString对象中与要求的字符匹配的最后一个字

    2022年6月29日
    40

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号