-
python的语言特性
- python是动态语言。动态还是静态指的是编译期还是运行期确定的类型,动态语言在编译时变量的数据类型即可确定。多数静态类型语言要求在使用变量之前必须声明数据类型,某些具有类型推导能力的现代语言可能能够部分减轻这个要求。编译期是指编译期把源代码翻译成计算机能够识别的代码。而运行期就是代码跑起来了,被装载到内存中去了。
- python是强类型语言。强类型指的是不会发生隐式类型转换。
1 + '1' # 弱类型语言不会报错(隐式转换),强类型报错
-
鸭子类型
“当看到一只鸟走起来像鸭子,游泳起来像鸭子,叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”
根据面向对象而言,一个对象的类型由其父类决定。但是python不一样,python解释器更关注的是接口也不是类型,即对象的类型不再由继承等方式决定,而由实际运行时所表现出的具体行为来决定。
以下参考这篇blog:python鸭子类型博客
在Python中,面向对象的多态和抽象是通过把协议当作正式接口来实现的。想让这个类成为相对应的鸭子类型,就要实现相关的协议,即相关的__method__。
例如基本序列协议: 实现__len__和__getitem__方法,就可以索引进行随机访问list[index]
和通过len
得到长度。实现了__item__, __next___就可以得到一个迭代器。这些界定类型方式并不是通过继承来判断,而是根据所实现的方法(即表现出来的行为)Python中,通过对魔法函数的实现,从而获得Python原生的支持,其中对一元中缀表达式的覆盖,对常规加法操作、乘法操作等的覆盖,皆是鸭子类型的表现形式。
鸭子类型的编程风格,在实际的应用场景中,发挥的是类似一种面向对象中多态的功能。例子可以看上面博客中,电子商务的应用场景。
关于魔法函数一览,参考https://zhuanlan.zhihu.com/p/26488074
-
猴子补丁
属性在运行时的动态替换,叫做猴子补丁(Monkey Patch)。在不修改程序原本代码的前提下,通过添加类或模块等方式在程序运行过程中加入代码,起到一个类似补丁的作用。但是添加模块用法比较复杂,添加类方法或者属性比较简单,可看以下例子:
class Foo(object):
def bar(self):
print 'Foo.bar'
def bar(self):
print 'Modified bar'
Foo().bar() # 输出Foo.bar
Foo.bar = bar # 猴子补丁
Foo().bar() # 输出Modified bar
一个实际应用的例子,首先要知道obj.gatattr()方法,类似还有setattr和hasattr
# 用于返回一个对象属性值
getattr(object, name[, default]) # obj对象,name对象属性名,第三个参考括号指可以木有
# default -- 默认返回值,如果不提供该参数,在没有对应属性时,将触发 AttributeError。
# 如果对象有该属性返回 True,否则返回 False
hasattr(object, name)
# 给对面的属性设置值,这个属性可以不存在
setattr(object, name, value)
# 返回实例的类
obj.__class__
例子blog:猴子补丁实际例子blog
-
自省
在计算机编程中,自省是指这种能力:检查某些事物以确定它是什么(类型)、它知道什么(属性)以及它能做什么(方法)。自省向程序员提供了极大的灵活性和控制力。
关于类,最基础的一些自省语法
# isinstance函数来判断一个对象是否是一个已知的类型
isinstance(object, classinfo) # obj实例对象,classinfo类名或者它们组成的元祖
# case
isinstance (a,(str,int,list)) # 是元组中的一个返回 True
# isinstance考虑继承关系,与type有区别
# 如果要判断两个类型是否相同推荐使用 isinstance()
class A:
pass
class B(A):
pass
isinstance(A(), A) # returns True
type(A()) == A # returns True
isinstance(B(), A) # returns True
type(B()) == A # returns False
# 另外关于访问对象的属性 hasattr getattr setattr 可以看上面的case
id(obj) # 返回一个地址
# == 一般是值判断, is 一般是地址判断
class Book:
def test(self):
pass
book = Book()
print([] == None)
print([] == Flase) # Flase,值并不相等!
print(book.test() == None) # None 等于None , 值相等
print(book.test() is None) # None只有一个,函数无返回时默认返回None
# 前两输出Flase, 后两输出都是Ture
# 跟Flase Ture None 判断都是用is最标准
# 一般的逻辑判断,并不代表就[]就是Flase,而是逻辑上是flase
详细自省:自省指南
-
列表推导式与生成器
a = [1, 2, 3]
b = ['a', 'b', 'c']
# 生成dict
d = {k: v for k, v in zip(b, a)} # zip成对迭代
d2 = {k: v for k in b for v in a}
# d 和 d2 一样生成了字典
# 推导式中添加过滤条件
case = [x for x in range(10) if x%2] # 奇数过滤
a = [x for x in range(10)] # 列表推导式
l = (x for x in range(10)) # 生成器
print(type(l)) # <class 'generator'>
# 值得注意的是生成器比列表推导式更节省内存
通过列表生成式可以直接生产一个列表,但是当我们数据很多的时候,就很浪费内存,特别是我们只需要其中几个元素的时候。因此当列表的元素可以通过某种算法全部迭代出来的时候,我们就不必创建完整的列表,这种一遍迭代一遍计算的机制叫做生成器generator。生成器比列表更节约内存,因为它不是一次性生产大量数据,因此数据量较大时,生成器是更好的选择。
因为生成器是懒加载的,因此它具有节约内存的特点。
生成器的语法有两种:
1.跟列表推导式类似,只不过用括号代替了[]
2.利用yield语法。相当于一个中断,把右边的值或者表达式的结果返回,下一次从左边开始执行。辅以next和send可以实现更多奇淫巧技。
yield与next配合使用,生成器内部保存了所有临时变量和环境,在yield处回到当时的状态,通过next来继续执行。与普通的函数调用相比,特殊的地方就是保存调用环境。详细用法参考生成器部分
生成器就是可以生产值的函数,当一个函数有了yield关键字就成了生成器,生成器可以挂起执行并且保持当前执行的状态。在Flask应用里面,有一个很经典的应用场景。算了。
class SQLAlchemy(_SQLAlchemy):
# 用contextmanager简化了每次commit的try和except的处理
@contextmanager
def auto_commit(self):
try:
yield # yield 相当于一个中断返回点
self.session.commit() # db提交
except Exception as e:
self.session.rollback() # db提交后防止出错需要回滚
raise e
# 外面调用
with db.auto_commit():
my_community = Community()
my_community.set_attrs(form.data)
db.session.add(my_community)
-
迭起器与可迭代对象
可迭代对象,里面数据可以一个个取出来,像for循环取值一样。
可迭代协议:可以被迭代要满足要求的就叫做可迭代协议。内部实现了__iter__方法
# 判断是不是可迭代对象
from collections import Iterable
isinstance(obj, Iterable)
迭代器:实现了迭代器协议的对象,既内部实现了__iter__方法和__next__方法,生成器就是一种迭代器。
两者的差别在于:迭代器是惰性的,事先不知道长度,其元素是一个个迭代出来的。计算了前一个才知道下一个。因此,list,str和dict都是可迭代对象不是迭代器,可以通过iter来转化成可迭代器。
凡是可作用于for
循环的对象都是Iterable
类型;凡是可作用于next()
函数的对象都是Iterator
类型,它们表示一个惰性计算的序列;
关于迭代器用法参考:https://www.jianshu.com/p/0440bf135f37
里面详细解释了,for循环时,迭代器的机制。比如for i in obj, 会首先iter(obj)。其中需要obj实现了_iter_得到一迭代器。其中_iter_只执行一次,对迭代器对象进行初始化或者生成迭代器,然后返回迭代器。每一次for,其实都是调用了迭代器实现的_next_方法,而_next_方法每次都会返回一个值,这个值就是for循环每次得到的值。到了最后一次next的时候会抛出一个stopleration异常来终止for循环。
关于iter()函数:
iter函数可以返回一个迭代器,参考https://blog.csdn.net/hedan2013/article/details/55002389
关键是对于已经实现_iter_的对象和序列对象,都可以返回一个迭代器,用以for循环使用
关于迭代器,可迭代对象和生成器的关系
参考https://www.cnblogs.com/eastonliu/p/9156418.html
生成器是特殊的迭代器,通过yield实现了iter和next的效果,所以生成器一定是迭代器(反之不成立),可迭代对象是实现了_iter_,可以通过_iter_生成并返回了一个迭代器,而迭代器实现了_next_,就这么简单。所以说可迭代对象本身不是迭代器,但是可以生成一个迭代器。
-
python单元测试
单元测试就是针对程序模块进行正确性检验,比如对一个函数,一个类进行验证,这样从底层向上(每一个小单元代码)保证了整体程序的正确性。测试影响设计,容易测试的代码一般是高内聚低耦合(就是好的意思..)。一般利用一些单元测试的库,如pytest
-
python3改进(与python2一些区别)
- print成为函数
- 编码问题。python3不再有unicode对象,默认str就是Unicode
- 除法变化。python3除号返回浮点数
- 类型注解(type hint)
- 优化的super()方便直接调用父类函数
class Base: def hello(self): print('base:hello') class A(Base): def hello(self): super(A, self).hello() # py2 class B(Base): def hello(self): super().hello() # py3
- 高级解包操作。a,b,*rest = range(10)
a, b, *c = range(10) print(c) # c是一个列表,包括了剩下所有元素 c, d, *_ = range(10) # *_舍弃剩下部分
- 限定关键字参数 # 传入函数的参数app.run(debug=True) debug就是关键字参数
- 一切返回迭代器。没有xrange,只有range,而且py3的range不再返回list,而是一个懒加载可迭代对象,节省内存。
- python3新增:新增了asynio异步编程的库,还有enmu库等,性能优化