一摞python风格的纸牌
from collections import namedtuple
Card = namedtuple('Card', ['rank', 'suit']) # 构建只有少数属性但是没有方法的对象
class FrenchDeck:
ranks = [str(n) for n in range(2, 11)] + list('JQKA')
suites = '黑桃>红桃>方块>梅花'.split('>')
def __init__(self):
# 具名元祖实例化
self._cards = [Card(rank, suit)
for suit in self.suites
for rank in self.ranks]
def __len__(self):
return len(self._cards)
def __getitem__(self, item):
"""
可迭代
把[]操作交给self._cards列表,支持切片操作
:param item:
:return:
"""
# 具名元祖支持索引取值
return self._cards[item]
def spades_high(card):
"""
排序
2最小,A最大
黑桃>红桃>方块>梅花
:param card:
:return:
"""
suit_values = {
"黑桃": 3,
"红桃": 2,
"方块": 1,
"梅花": 0
}
rank_value = FrenchDeck.ranks.index(card.rank)
return rank_value * len(suit_values) + suit_values[card.suit]
if __name__ == '__main__':
c = Card(1, 2)
print(c[1])
f = FrenchDeck()
print(len(f)) # 52 = (11 - 2 + 4) * 4
print(f[0]) # Card(rank='2', suit='spades')
print(f[:3]) # [Card(rank='2', suit='spades'), Card(rank='3', suit='spades'), Card(rank='4', suit='spades')]
for i in f:
print(i)
for i in reversed(f):
print(i)
for i in sorted(f, key=spades_high):
print(i)
具名元祖
自 Python 2.6 开始,namedtuple 就加入到 Python 里,用以 构建只有少数属性但是没有方法的对象
在官方文档中也翻译为命名元祖
它赋予了每个位置一个含义,提供可读性
和自文档性
。
通过名字获取值
通过索引值获取值
导入方法
from collections import namedtuple
具名元祖源码阅读
返回一个新的元组子类,名为 typename
test = namedtuple('mytest','test1 test2')
t = test(1, 2)
type(t) # __main__.mytest
field_names
用空白或逗号分隔开元素名
if isinstance(field_names, str):
field_names = field_names.replace(',', ' ').split()
field_names = list(map(str, field_names))
rename
为真时, 无效字段名会自动转换成位置名。
有效字段名:除了下划线开头的那些任何有效的Python 标识符。有效标识符由字母,数字,下划线组成,但首字母不能是数字或下划线,另外不能是关键词
比如 ['abc', 'def', 'ghi', 'abc']
转换成 ['abc', '_1', 'ghi', '_3']
, 消除关键词 def
和重复字段名 abc
isidentifier
:判断字符串是否是有效的 Python 标识符,可用来判断变量名是否合法
iskeyword
:包含全部关键字的冻结的集合
from keyword import iskeyword as _iskeyword
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)
defaults
:默认值
test = namedtuple('mytest', 'test1 test2', defaults=[1, 2])
test().test1 # 1
test().test2 # 2
test = namedtuple('mytest', 'test1 test2 test3', defaults=[1, 2])
test(0).test1 # 0
test(0).test2 # 1
test(0).test3 # 2
module
:设置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
test = namedtuple('mytest', 'test1 test2 test3', module='aaa')
type(test(1, 2, 3)) # aaa.mytest
_make
:类方法从存在的序列或迭代实例创建一个新实例
@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
Point = namedtuple('Point', ['x', 'y'])
t = [11, 22]
Point._make(t)# Point(x=11, y=22)
_asdict
:返回一个新的字典
def _asdict(self):
'Return a new dict which maps field names to their values.'
return _dict(_zip(self._fields, self))
p = Point(x=11, y=22)
p._asdict()# {'x': 11, 'y': 22}
_replace
将指定域替换为新的值
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
p = Point(x=11, y=22)
p._replace(x=33) # Point(x=33, y=22)
_fields
:列出字段名
p._fields #('x', 'y')
_field_defaults
:字典将字段名称映射到默认值
Account = namedtuple('Account', ['type', 'balance'], defaults=[0])
Account._field_defaults# {'balance': 0}
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(field_names):'(\'rank\', \'suit\')'
# .replace("'", "")[1:-1]:变为 'rank, suit'
repr_fmt = '(' + ', '.join(f'{name}=%r' for name in field_names) + ')'
# '(rank=%r, suit=%r)'
tuple_new = tuple.__new__ 'rank, suit'
_dict, _tuple, _len, _map, _zip = dict, tuple, len, map, zip
# 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}))' # __new__的时候将内容变为元祖,后续访问的时候可以用索引来查找内容
namespace = {'_tuple_new': tuple_new, '__name__': f'namedtuple_{typename}'}
# Note: exec() has the side-effect of interning the field names
exec(s, namespace)# 动态执行 Python 代码 补充namespace的__new__方法
__new__ = namespace['__new__']
__new__.__doc__ = f'Create new instance of {typename}({arg_list})' # 为__new__方法添加说明
if defaults is not None:
__new__.__defaults__ = defaults
@classmethod
def _make(cls, iterable): # 定义_make方法
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')# 为_make添加说明
def _replace(self, /, **kwds): # 定义_replace方法
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')# 为_replace方法添加说明
def __repr__(self):# 定义__repr__方法 在print的时候调用
'Return a nicely formatted representation string'
return self.__class__.__name__ + repr_fmt % self
def _asdict(self): # 定义_asdict方法 转换成字典输出
'Return a new dict which maps field names to their values.'
return _dict(_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,
'_field_defaults': field_defaults,
# alternate spelling for backward compatibility
'_fields_defaults': field_defaults,
'__new__': __new__,
'_make': _make,
'_replace': _replace,
'__repr__': __repr__,
'_asdict': _asdict,
'__getnewargs__': __getnewargs__,
}
for index, name in enumerate(field_names):
doc = _sys.intern(f'Alias for field number {index}')
class_namespace[name] = _tuplegetter(index, 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:# 使用默认module名称
try:
module = _sys._getframe(1).f_globals.get('__name__', '__main__')
except (AttributeError, ValueError):
pass
if module is not None:# 重新命名module名称
result.__module__ = module
return result
使用
Card = namedtuple('Card', ['rank', 'suit'])
在通过名字获取值的方式上类似于
class Card:
def __init__(self, rank, suit):
self.rank = rank
self.suit = suit
列表推导式
[表达式 for 变量 in 列表]
[表达式 for 变量 in 列表 if 条件]
排序
sort 是应用在 list 上的方法,sorted 可以对所有可迭代的对象进行排序操作。
list 的 sort 方法返回的是对已经存在的列表进行操作,而内建函数 sorted 方法返回的是一个新的 list,而不是在原来的基础上进行的操作。
sort
In[1]:a = [3, 2, 1]
In[2]:a.sort()
In[3]:a
Out[3]:[1, 2, 3]
sorted
key 指定带有单个参数的函数,用于从 iterable 的每个元素中提取用于比较的键 (例如 key=str.lower
)。默认值为 None
(直接比较元素)。
In[1]:sorted([3, 2, 1])
Out[1]:[1, 2, 3]
魔方方法
__repr__
和__str__
repr
:在repr()
方法中使用。当没有实现该方法的时候,打印实例可能为str
:在str()
方法中使用,在print()
方法中使用
当一个对象没有__str__
的时候会调用__repr__
自己定义__bool__
方法
如果没有定义的话,在执行bool(x)
的时候默认会调用bool(x.__len__())