[实验楼]python11期--NO.1(未完成)
tuple 是 Python 的一种不可变数据类型,
用于列表数据一旦初始化就不会再修改的场景。
tuple 只能通过位置索引去访问里面的元素,
但有时候我们需要给每个元素取个别名,
以便通过别名去获取对应的元素。本次挑战就是需要大家自己来实现一个简单的命名 Tuple。
功能点
模块文件保存路径为 /home/shiyanlou/namedtuple.py
模块内实现一个 NamedTuple 类,其构造函数接受 iterable 和 fields 两个参数,分别用于传递数据及其对应的别名
NamedTuple 需要支持通过位置索引和别名属性两种方式去获取数据
NamedTuple repr 输出格式类似于”NamedTuple(x=1, y=2)“,其中 x、y 是别名,1、2 是数据。
不能使用 Python 标准库里的实现,代码里不能出现 namedtuple 相关字样
NamedTuple 类可以继承于 tuple,但需要注意 tuple 为不可变数据类型,需要覆盖其 __new__ 方法
为了得到想要的 repr 输出,需要实现 __repr__ 方法
import unittest
from namedtuple import NamedTuple
class TestNamedTuple(unittest.TestCase):
def test_features(self):
fields = ['x', 'y']
values = [1, 2]
nt = NamedTuple(values, fields)
self.assertEqual(nt[0], 1)
self.assertEqual(nt[1], 2)
self.assertEqual(nt.x, 1)
self.assertEqual(nt.y, 2)
self.assertEqual(repr(nt), 'NamedTuple(x=1, y=2)')
_nt_itemgetters = {} def namedtuple(typename, field_names, *, rename=False, defaults=None, 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 = _sys.intern(str(typename)) 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] = f'_{index}' seen.add(name) for name in [typename] + field_names: 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 ' f'identifiers: {name!r}') if _iskeyword(name): raise ValueError('Type names and field names cannot be a ' f'keyword: {name!r}') seen = set() for name in field_names: if name.startswith('_') and not rename: raise ValueError('Field names cannot start with an underscore: ' f'{name!r}') if name in seen: raise ValueError(f'Encountered duplicate field name: {name!r}') seen.add(name) field_defaults = {} if defaults is not None: defaults = tuple(defaults) if len(defaults) > len(field_names): raise TypeError('Got more default values than field names') field_defaults = dict(reversed(list(zip(reversed(field_names), reversed(defaults))))) # Variables used in the methods and docstrings field_names = tuple(map(_sys.intern, field_names)) num_fields = len(field_names) arg_list = repr(field_names).replace("'", "")[1:-1] repr_fmt = '(' + ', '.join(f'{name}=%r' for name in field_names) + ')' tuple_new = tuple.__new__ _len = len # Create all the named tuple methods to be added to the class namespace s = f'def __new__(_cls, {arg_list}): return _tuple_new(_cls, ({arg_list}))' namespace = {'_tuple_new': tuple_new, '__name__': f'namedtuple_{typename}'} # Note: exec() has the side-effect of interning the field names exec(s, namespace) __new__ = namespace['__new__'] __new__.__doc__ = f'Create new instance of {typename}({arg_list})' if defaults is not None: __new__.__defaults__ = defaults @classmethod def _make(cls, iterable): result = tuple_new(cls, iterable) if _len(result) != num_fields: raise TypeError(f'Expected {num_fields} arguments, got {len(result)}') return result _make.__func__.__doc__ = (f'Make a new {typename} object from a sequence ' 'or iterable') def _replace(_self, **kwds): result = _self._make(map(kwds.pop, field_names, _self)) if kwds: raise ValueError(f'Got unexpected field names: {list(kwds)!r}') return result _replace.__doc__ = (f'Return a new {typename} object replacing specified ' 'fields with new values') 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) # Modify function metadata to help with introspection and debugging for method in (__new__, _make.__func__, _replace, __repr__, _asdict, __getnewargs__): method.__qualname__ = f'{typename}.{method.__name__}' # Build-up the class namespace dictionary # and use type() to build the result class class_namespace = { '__doc__': f'{typename}({arg_list})', '__slots__': (), '_fields': field_names, '_fields_defaults': field_defaults, '__new__': __new__, '_make': _make, '_replace': _replace, '__repr__': __repr__, '_asdict': _asdict, '__getnewargs__': __getnewargs__, } cache = _nt_itemgetters for index, name in enumerate(field_names): try: itemgetter_object, doc = cache[index] except KeyError: itemgetter_object = _itemgetter(index) doc = f'Alias for field number {index}' cache[index] = itemgetter_object, doc class_namespace[name] = property(itemgetter_object, doc=doc) result = type(typename, (tuple,), class_namespace) # 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
Win a contest, win a challenge
posted on 2018-04-25 16:30 pandaboy1123 阅读(186) 评论(0) 编辑 收藏 举报