import collections
Card = collections.namedtuple('Card',['rank','suit'])
class FrenchDeck:
ranks = [str(n) for n in range(2,11)] + list('JQKA')
suits = 'spades diamonds clubs hearts'.split()
def __init__(self):
self._cards = [Card(rank,suit) for suit in self.suits for rank in self.ranks ]
def __len__(self):
return len(self._cards)
def __getitem__(self,position):
return self._cards[position]
obj[key] <=> obj.__getitem__(key)
实际上背后实现的原理就是这样的(当碰到特殊的句法的时候Python回使用特殊的方法去激活这些操作)
这些方法能让语言支持和实现以下的一些特点:(迭代、集合类、属性访问、运算符重载、函数和方法的调用、对象创建和销毁、字符串的形式化和格式化、管理上下文with)
在以上的程序中,实现了__getitem__
的方法,然后就把这个[]
索引操作给设定下来了,因为self._card
s是列表,所以列表支持索引、支持反向索引、还支持迭代 如果没有实现这个方法就会报错:‘FrenchDeck’ object does not support indexing
迭代通常是隐式的,我们并没有实现__contains__
这个方法,那么in操作符会按顺序做一次迭代搜索,于是in运算符就可以使用for .. in ..
就是迭代
再次声明一点、特殊方法是被python解释器去调用的,我们本身不需要list.__len__()
,可以直接使用len(对象),如果是内置数据类型的调用的话,直接就读取PyVarObject的obj_size 属性,读取一个值比调用方法更快。
特殊方法的调用很多都是隐式的,比如for i in x
:实际上背后调用的是iter(x)
,我们调用特殊方法的频率要远低于我们去实现它的次数。除了__init__
,是为了再子类中调用超类的构造方法。
for x in items
隐式调用了__contains__
,__iter__
.if not while not a
调用了__bool__
如果没有实现就用__len__
. 许多方法都对应了其隐式的方法。with .. open ..
上下文管理器调用了__enter__
__exit__
。s = Solution(x,y) 就是调用了初始化方法 而且还调用了__new__方法创建一个实例对象。[]
索引操作给设定下来了,因为self._card
s是列表,所以列表支持索引、支持反向索引、还支持迭代 如果没有实现这个方法就会报错。__setitem__赋值的时候就会这样。from math import hypot
class Vector:
def __init__(self,x=0,y=0):
self.x = x
self.y = y
def __repr__(self):
return 'Vector(%r,%r)'%(self.x,self.y)
def __abs__(self):
return hypot(self.x,self.y)
def __bool__(self):
return bool(abs(self))
def __add__(self,other):
x = self.x + other.x
y = self.y + other.y
return Vector(x,y) #不改变对象本身 只是单纯返回值
def __mul__(self,scalar):
return Vector(self.x*scalar,self.y*scalar) #乘法交换律被忽略了,在
Python的一个内置函数__repr__
是能把一个对象利用字符串的形式表达出来,当我们不设定是默认返回对象类别及地址
__repr__
和 __str__
的区别在于后者只有在print()
到终端的时候或者str()
函数调用的时候才会被调用,__repr__
是最后保障,因为当没有实现__str__
时,系统会调用__repr__
https://stackoverflow.com/questions/1436703/difference-between-str-and-repr-in-python 回答得很精彩
bool
类型,python中任何要判断的地方如if while not and or
都要触及__bool__
这个方法,如果对象不存在这个方法,python回去触发obj.__len__()
这一个方法。如果为空则为空
一个有83个特殊方法,其中有47个用于算术符、位运算、比较操作。
通过实现特殊方法,自定义的数据结构类型可以表现得跟python内置的数据类型一样,从而让我们写出更具有Python风格的代码。
1、数据模型、对象模型
2、元对象所指的是那些对建构语言本身很重要的对象,以此为前提,协议可以看做接口。也就是说,元对象协议是对象模型的同义词,他们的意思都是构建语言的核心API。
__iter__ 、__len__ 、__contains__
支持不同的操作符,其继承逻辑如下:u'一般前面加个u'
str
utf8的形式可以支持中文。[i for i in range(5)] [[i for i in range(4)] for j in range(5)] [i for i in range(5) if i%2==1]
可以取代map\filter
等操作。{a:b for a in x for b in c} {a:b for a,b in q}自动解包
(a for a in c)
迭代器就可以利用生成器懒加载的特性来提高效率。#列表推导能做 filter 和map函数做的事,而且不用借助lambda
symbols = "$#%^$@*&"
beyond_asic = [ord(s) for s in symbols if ord(s) > 127]
beyond_asic1 = list(filter(lambda c :c > 127,map(ord,symbols)))
card = ['A','K','Q']
suit = ['diamond','club','hearts','spades']
result = [(k,v) for k in card for v in suit]#笛卡尔积的感觉
num = {(a,b) for a in range(10) for b in range(5)} #生成器表达式 列表推导换成这样
*
来表示忽略个数。还可以用星号*
来把可迭代对象拆开作为函数的参数。用*
来处理剩下的元素,几种精力在我们想要的元素上,**
是字典的不定长参数的表示。metro_areas = [('Tokyo','JP',36.933,(35.689722,139.691667)), # ➊
('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)),
('Mexico City', 'MX', 20.142, (19.433333, -99.133333)),
('New York-Newark', 'US', 20.104, (40.808611, -74.020386)),
('Sao Paulo', 'BR', 19.649, (-23.547778, -46.635833)),
]
print('{:15} | {:^9} | {:^9}'.format('', 'lat.', 'long.'))
fmt = '{:15} | {:9.4f} | {:9.4f}'
for name, cc, pop, (latitude, longitude) in metro_areas: # ➋ #解包
if longitude <= 0: # ➌
print(fmt.format(name, latitude, longitude))
(_asdict\_make()\_fields)
list int
等操作。from collections import namedtuple
City = namedtuple('City','name country population position')
tokoyo = City('Tokyo','Japan','2000,00',(32.56,42.005))
tokoyo._asdict()
切片,s[a?️c] 其中a是其实 b是终止 c是步长 当我们操作lis[a?️c]的时候,会创建一个切片对象slice(a?️c) 然后传入 lis.getitem(slice(start,stop,step)) 的操作,我们还可以为切片加上名字。如下:
invoice = """
... 0.....6................................40........52...55........
... 1909 Pimoroni PiBrella $17.50 3 $52.50
... 1489 6mm Tactile Switch x20 $4.95 2 $9.90
... 1510 Panavise Jr. - PV-201 $28.00 1 $28.00
... 1601 PiTFT Mini Kit 320x240 $34.95 1 $34.95
... """
SKU = slice(0, 6)
DESCRIPTION = slice(6, 40) #切片命名
lines = invoice.split('\n')
for line in lines:
print(line[DESCRIPTION])
[[for_ in range(5)] for _ in range(5)]
)生成。也就是浅复制和深复制的区别*,元组中内存地址是一样的,改一个就改了全部了。虽然元组是不可变的、但是元组中的元素时可以变的。序列的增量赋值,+=
是取决于特殊的方法 __iadd__(self)
如何这个没有被实现的话,那么就会调用**add方法,如果调用iadd的话,那么a就会改动,相当于a.extends(b)如果没有调用的话a就不会被改动,没有新对象生成。对不可变对象进行拼接操作效率会变低,因为每次都会有一个新的对象产生销毁str除外**。
排序 list.sort() 返回的是None 在Python中如果某个操作是就地操作,不会返回新对象的时候,就会返回None 来提醒你
str里面的所有方法就都有返回新对象,可以将其串联起来。 相反sorted会返回一个新对象。
对Python可视化原理进行分析
bisect 模块的使用 以及fmt可视化漂亮输出。Python 二分查找与 bisect 模块
总结:
1.列表很少放入不同类型的对象,而元组则相反,经常放入同类型的元素,列入列表里面存入都会呱呱叫的动物。
2.list.sort sorted max min key参数很巧妙,就好像java中的compartor一样,自定义比较方法,一般配合lambda表达式使用。
元组和列表的区别就是可变和不可变具体底层魔术方法的实现差异如下:
元组可以索引,但是不可以改变。
创建双精度array数组
dict\defaultdict\OrderedDict
前一个是基本的,后两个是在collections模块里面的变种dd = defaultdict(int)
dd.get("a")# 还是None
dd["a"] #这个时候就会调用missing逻辑 然后返回int 0 [] 空列表
#定义一个新的字典,即时在访问数字的情况下把数字变成字符串来访问。
class StrKeyDict0(dict):
def __missing__(self,key):
if isinstance(key,str):#必须的,因为str部分已经判断过了,如果没有的话就会把一个逻辑弄乱
raise KeyError(key)
return self[str(key)]
def get(self,key,default=None):
try:
return self[key]
except KeyError:
return default
def __contains__(self,key):
return key in self.keys() or str(key) in self.keys
import builtins
from collections import ChainMap
a = ChainMap(locals(),globals(),vars(builtins))
from types import MappingProxyType
d = {'a':[3,5]}
d_unchange = MappingProxyType(d)
d_unchange['a'].append('a')
d[2]='B'
d_unchange['2'] = 'b'#是不可以添加键值的,一旦建立就不可以更改。
集合有交集并集的散列对象,操作十分快。{}来初始化一个集合.set的一些操作交并合对称
set、dict背后:散列表其实是一个稀疏数组,在数据结构教材中,每个键值对占一个表元(散列表中的单元)每个元有2个部分,一个是对键的引用,一个是对值的引用。散列表的作用就是快速存储地址的一个地方,**在字典中流程:首先计算键的散列值-》使用散列值部分数来定位一个表元-》表元为空抛出异常,否则比较键是否相等, 不相等就使用散列值的另一部分定位散列表中的一行。dict的实现及结果就是:1、键必须是可散列的2、字典在内存上耗费巨大3、键查询很快4、键的次序取决于添加次序5、往字典里添加新键可能会改变自己键的顺序。
**
dict实现了基本的3个魔术方法以及Mapping的一些专有方法、其中__eq__\__ne__
比较重要
更新值的时候d[“k”]=xx
也就是说**getattr是用来抛出异常的最后一步到这里,当找不到属性时,也就是getattribute没有找到相应结果时就会抛出相应的异常,一般就写getattr**来实现动态加载。
可以使对象表现出数值类似__str__
描述器就是data descriptor 是数据描述器
class Meter(object):
"""
对于单位"米"的描述器
"""
def __init__(self, value=0.0):
self.value = float(value)
def __get__(self, instance, owner):
return self.value
def __set__(self, instance, value):
self.value = float(value)
class Foot(object):
"""
对于单位"英尺"的描述器
"""
def __get__(self, instance, owner):
return instance.meter * 3.2808
def __set__(self, instance, value):
instance.meter = float(value) / 3.2808
class Distance(object):
"""
用米和英寸来表示两个描述器之间的距离
"""
meter = Meter(10)
foot = Foot()
装饰器
也是一个实例@property\@staticmethod\@classmethod\@asynico.cortine
。python没有new关键字,类初始化的时候实际就是__new__了一个实例,然后初始化,最后返回那个实例的。
import random
class BingoCase:
def __init__(self,items):
self._items = list(items)
random.shuffle(self._items)
def pick(self):
try:
return self._items.pop()
except IndexError:
raise LookupError("pick from empty Bingocase")
def __call__(self):
return self.pick()
a = BingoCase([3,5,7,2])
a()#把一个实例变成可调用的对象
__dict__
函数使用这个属性来存储赋予它的用户属性。__annotations__
可以在函数定义的时候添加注解同时也返回注解、不作任何检查例如def getMin("str"->s) -> "str"
有点类似java中声明类型。__slots__
是类属性,里面包含__dicts__
当实例很多的时候也用__slots__来专门声明。# lambda a,b:a*b #这么一个简单的函数配上reduce就是神器
from functools import reduce
a = reduce(lambda a,b:a * b,range(1,5))
本章主要介绍了Python函数的一些特性例如:把函数当做一个变量传入参数、返回一个函数、7种可调用的对象。高阶函数有map\filter\max\sorted\min
operator Mul、和functools的partial、reduce、partialmethod
函数内省
help\dir\ 都比较好用
函数注解的方式
虽然设计模式跟所用的语言无关,但并不意味着每一个模式都能在每一门语言中使用。
策略模式:6-1表示出了策略模式对类的编排。
定义:定义一系列的算法,把他们一一封装起来,并且使他们可以相互替换,本模式使得算法可以独立于使用它的客户而变化。
根据客户的属性或订单中的商品来计算折扣。
例如:
from abc import ABC,abstractmethod
from collections import namedtuple
Customer = namedtuple("Customer","name fidelity")#客户加积分
class LineItem:#组成购物车
def __init__(self,product,quantity,price):
self.product = product
self.quantity = quantity
self.price = price
def total(self):
return self.price * self.quantity
class Order:#上下文也就是订单 用户行为 有车 有打折 有用户情况
def __init__(self,customer,cart,promotion=None):#cart是购物车
self.customer = customer
self.cart = list(cart)
self.promotion = promotion
def total(self):
if not hasattr(self,'__total'):
self.__total = sum(item.total() for item in self.cart)
return self.__total
def due(self):
if self.promotion is None:
discount = 0
else:
discount = self.promotion.discount(self)#传入一个order
return self.total() - discount
def __repr__(self):
fmt = ''
return fmt.format(self.total(),self.due())
class Promotion(ABC):
@abstractmethod
def discount(self,order):
"返回折扣金额(正值)"
class FidelityPromo(Promotion):
"积分1000或1000以上的用户"
def discount(self,order):
return order.total() * 0.05 if order.customer.fidelity >= 1000 else 0
class BulkItemPromo(Promotion):
"单个商品20个或以上的订单"
def discount(self,order):
discount = 0
for item in order.cart :
if item.quantity >= 20:
discount += item.total() * 0.1
return discount
class LargeOrderPromo(Promotion):
def discount(self,order):
discount_items = {item.product for item in order.cart}
if len(discount_items) >= 10:
return order.total() * 0.07
return 0
joe = Customer('john Doe',0)
ann = Customer("Ann Smith",1100)
cart = [
LineItem('banana',4,0.5),
LineItem('appple',10,1.5),
LineItem('watermellon',5,5.0)
]
Order(joe,cart,FidelityPromo())
Order(ann,cart,FidelityPromo())
banana_cart = [
LineItem('banana',30,0.5),
LineItem('apple',10,1.5)
]
Order(joe,banana_cart,BulkItemPromo())
函数作为参数传入不需要创建类来实例化对象传入。
class Order:#上下文
def __init__(self,customer,cart,promotion=None):#cart是购物车
self.customer = customer
self.cart = list(cart)
self.promotion = promotion
def total(self):
if not hasattr(self,'__total'):
self.__total = sum(item.total() for item in self.cart)
return self.__total
def due(self):
if self.promotion is None:
discount = 0
else:
discount = self.promotion(self)#传入一个order
return self.total() - discount
def __repr__(self):
fmt = ''
return fmt.format(self.total(),self.due())
def fidelity_promo(order):
"为积分有1000的打5%的折扣"
return order.total() * 0.05 if order.customer.fidelity >= 1000 else 0
def bulk_item_promo(order):
discount = 0
for item in order.cart:
if item.quantity >= 20:
discount += item.total() * 0.1
return discount
def large_order_promo(order):
discount_items = {item.product for item in order.cart}
if len(discount_items) >= 10:
return order.total() * 0.07
return 0
@decorate
def target():
print("running target")
#等同于
target = decorate(target)
#可以理解为一个功能函数在需要的时候,可以装饰任何函数,把该函数作为参数传入
registry = []
def register(func):
print("running retister(%s)"%func)
registry.append(func)
return func
@register
def f1():
print("running f1()")
@register
def f2():
print("running f2()")
@register
def f3():
print("running f3()")
#在上节中只需要把每个促销函数加上一个装饰器
promos = []#利用装饰器来添加促销函数(对象)也就是策略
def register(func):
promos.append(func)
return func
@promotion
def fidelity(order):
return order.total() * 0.05 if oder.customer.fidelity >= 1000 else 0
def best_promo(order):
return max(promo(order) for promo in promos)
from dis import dis
b = []
def f1(a):
print(a)
print(b)
b.append(3)
class Averager():
def __init__(self):
self.series = []
def __call__(self,new_value):
self.series.append(new_value)
total = sum(self.series)
return total/len(self.series)
def make_averager():
series = []#闭包
def averager(new_value):
series.append(new_value)#自由变量
total = sum(series)
return total/len(series)
return averager
def make_averager():
count = 0
total = 0
def averager(new_value):
nonlocal count,total#把变量标记为自由变量:指的是未在本地作用域中绑定的变量
count += 1
total += new_value
return total/count
return averager#在闭包中,对于不可变类型 数字 字符串、元组、集合这样的类型来说 要声明nonlocal才能对其操作
#实现一个简单的装饰器
def clock(func):
def clocked(*args):
t0 = time.perf_counter()
result = func(*args)
elapsed = time.perf_counter() - t0
name = func.__doc__
arg_str = ','.join(repr(arg) for arg in args)
print("[%0.8fs]%s(%s)->%r"%(elapsed,name,arg_str,result))
return result
return clocked
import time
@clock
def snooze(seconds):
time.sleep(seconds)
@clock
def factorial(n):
"doc of fucn"
return 1 if n<2 else n*factorial(n-1)
import time
import functools
def clock(func):
@functools.wraps(func)
def clocked(*args,**kwargs): #支持关键字参数了
t0 = time.time()
result = func(*args,**kwargs)
name = func.__name__
elapsed = time.time() - t0
arg_lst = []
if args:
arg_lst.append(','.join(repr(arg) for arg in args))
if kwargs:
pairs = ['%s=%r'%(k,w) for k,w in sorted(kwargs.items())]
arg_lst.append(','.join(pairs))
arg_str = ','.join(arg_lst)
print('[%0.8fs]%s(%s)->%r'%(elapsed,name,arg_str,result))
return result
return clocked
@property.setter、@property.getter
可以实现只读属性、一个是类方法、一个是变成普通方法。11. 叠放装饰器,也就是装饰器里面有装饰器。
@d1
@d2
def f1(): =>d1(d2(f1))
12.还可以初始化带参数的装饰器,例如可以设置一个参数active=True 来判断是否启用装饰器。
要深入理解装饰器,要区分运行时和导入时,还要知道变量的作用域(不可变、可变)、闭包、和nonlocal声明,掌握nonlocal和闭包不仅对构建装饰器有帮助,还能协助你在构建GUI程序时面向事件编程,或者使用回调处理异步I/O
functool.lru_cache\functools.wraps\functools.singledispatch()
一个是函数中有函数表示 外层不可变对象的值使其自由。