Python相关知识点准备
一、Python数据类型
- Python 数据模型其实就是对Python框架的综述,它规范了这门语言的自身构建模块,包括序列、迭代器、函数、类和上下文管理器。
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__
,是为了再子类中调用超类的构造方法。
1.1 有哪些地方隐式调用了特殊方法?
for x in items
隐式调用了__contains__
,__iter__
.if not while not a
调用了__bool__
如果没有实现就用__len__
. 许多方法都对应了其隐式的方法。with .. open ..
上下文管理器调用了__enter__
__exit__
。s = Solution(x,y) 就是调用了初始化方法 而且还调用了__new__方法创建一个实例对象。
1.2 __repr__和__str___有什么关系?
- 他们都是表示一个对象的方式,在输出到控制台或者其他设备时,调用,如果没有实现__str__就会调用__repr__方法,返回的是对象的hashcode 也就是地址。
1.3 obj[key] <=> obj.getitem(key) 索引访问的原理是什么?
- 把这个
[]
索引操作给设定下来了,因为self._card
s是列表,所以列表支持索引、支持反向索引、还支持迭代 如果没有实现这个方法就会报错。__setitem__赋值的时候就会这样。
1.4 .操作点操作的访问过程是什么?
- 访问属性、访问方法、访问实例属性、访问类属性、访问数据描述符(覆盖)、访问非数据描述符、就是动态属性的一个访问过程。
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。
1.4 特殊方法一览:(字符串的、数值的、集合的、迭代、属性管理的、实例创建与销毁的、属性描述符)
二、序列构成数组
- Python现在的风格:序列的泛型操作、内置的元组和映射类型、用缩进来架构的源码、无需声明变量的强类型。深入理解Python中的不同序列类型,不但能让我们避免重复造轮子、他们的API定义还能帮助我们定义自己的API ,设计得跟原生或者兼容未来可能会出现的类型。
2.1 Python中的序列分为可变序列和不可变序列,可以简单说一下嘛?
- 不可变:元组、基本数字、str、bytes
- 可变:list、bytearay、memoryview等,都实现了三个基本的协议就是
__iter__ 、__len__ 、__contains__
支持不同的操作符,其继承逻辑如下: - UML图显示了Python中序列的划分,可以分为可变序列和不可变序列。大概共同的方法如下:
2.2 python2和python3有设么区别吗?
- 字符串编码上 在Python2中,有两种字符串类型:str类型和Unicode类型。str就是难以懂的字节数据,unicode就是可以解码用utf-8解码的编码才能正常显示中文、
u'一般前面加个u'
python3中默认把字节数据编码成str
utf8的形式可以支持中文。 - 在python2中的列表推导式会发生内存泄露、python3修复了这个问题。
2.3 python中类似列表推导式的还有那些?通常用来做什么?
- 列表推导式
[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)
迭代器就可以利用生成器懒加载的特性来提高效率。 - 列表、元组、字典推导 来生成列表、元组、字典的方式更加易读高效率、但是当代码超过3行的时候就要考虑用for循环来写程序了。(在Python2.7中列表推导没有自己的局部变量域,会使上一个同名变量发生改变,出现变量泄露的情况,在Python3之后就不会有了。)
- 列表推导和map、filter 效率比较https://github.com/fluentpython/example-code/blob/master/02-array-seq/listcomp_speed.py
#列表推导能做 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)} #生成器表达式 列表推导换成这样
2.4 生成器迭代器和列表推导式的区别以及表达方式
- 生成器表达式在初始化序列的时候,比列表推导更加好,因为前者节省了内存,而不是先把一切数据排列好再放入构造器中去完成。只需要把方括号变成圆括号就好了。生成器表达式遵循迭代器协议可以逐个产出元素。
2.5 python中的拆包和打包有了解吗?zip(*iterator)
- 拆包其实就是从元组里面逐个获取元素,zip/pack 就是打包成元组。https://www.python.org/dev/peps/pep-3132/ 任何可迭代对象都可以被拆包,但是唯一一个要求就是目标变量的个数要跟元素个数一致,或者用
*
来表示忽略个数。还可以用星号*
来把可迭代对象拆开作为函数的参数。用*
来处理剩下的元素,几种精力在我们想要的元素上,**
是字典的不定长参数的表示。
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))