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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • MySQL复制

    复制复制是从一个MySQL服务器(master)将数据拷贝到另外一台或多台MySQL服务器(slaves)的过程.复制是异步进行的–slaves服务器不需要持续地保持连接来接收master的数据.

    2021年12月25日
  • Spring Boot 2.x: 定时给对象发送天气

    Spring Boot 2.x: 定时给对象发送天气使用Java写一个定时给对象发送天气的功能前言技术栈快速创建实例pom.xml文件新建接收天气api的实体天气接口封装的天气api简单演示获取天气api与发送邮件的逻辑设置发送账号信息配置appliction.properties控制层启动类效果源码地址前言不知不觉,又到了雨季,你对象是不是经常忘记带伞呢,这个时候写一个自动定时发送邮件的程序,提醒她带伞,会不会对你崇拜有加呢,当然,如果你对象是一位攻城狮,当我没讲~技术栈SpringBoot2.3.1Jdk1.8Maven快速创

    2022年7月27日
  • jsonobject详解_object转json字符串

    jsonobject详解_object转json字符串JSON(JavaScriptObjectNotation的首字母缩写)是一种轻量级数据交换格式,最常用于客户端-服务器通信。它既易于读/写,又与语言无关。JSON值可以是另一个JSON对象,数组,数字,字符串,布尔值(true/false)或null。也是后端开发中经常要接触的一个一个类,将要数据封装成json然后传递给前端。只要我们导入如下maven依赖即可使用由阿里巴巴发布的…

    2022年9月22日
  • maven找不到包但是确实引入了_idea写python好吗

    maven找不到包但是确实引入了_idea写python好吗直接上图,记住选中的位置要设置为Resouce,不然from导入时,会导致pycharm自动从cronjob根路径导入。放linux中运行时,涉及到调用的时候,就报错cronjob包找不到,因为只能找到当前执行的python文件下开始找,而不能找到父包设置位置在这里:…

    2022年8月25日
  • PyTorch 实现 ResNet34 分类(数据cifar10)「建议收藏」

    PyTorch 实现 ResNet34 分类(数据cifar10)「建议收藏」    又到整理的时候了,这次参考torchvision里面的resnet34源代码,自己修改了一下,实现cifar10数据集的分类任务。    其实网络上已经有很多优秀的源代码了,没必要再写,如果执意要说个理由的话,就当是自己的笔记了哈哈,方便以后使用可以快速查阅。没别的,菜鸟就应该多积累。ResNet34大体结构:图片:来自《深度学习框架PyTorch:入门与实践》PyTorch…

    2022年9月25日
  • 决心书

    决心书

    2022年3月6日

发表回复

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

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