基础概念相关
python中的类属性、实例属性、类方法、实例方法
- 实例属性和类属性重名
当实例属性和类属性重名时,实例属性优先级高,它将屏蔽掉对类属性的访问。
class Person(object):
address = 'zhejiang'
def __init__(self, name):
self.name = name
p1 = Person('Bob')
p2 = Person('Alice')
print 'Person.address = ' + Person.address # zhejiang
p1.address = 'shanxi'
print 'p1.address = ' + p1.address # shanxi
print 'Person.address = ' + Person.address # zhejiang
print 'p2.address = ' + p2.address # zhejiang
尾递归调用
- 一个简单的实现
def fact_iter(num, product):
if num == 1:
return product
return fact_iter(num - 1, num * product)
列表生成式
- [x * x for x in range(1, 11)]
- [x * x for x in range(1, 11) if x % 2 == 0]
- [m + n for m in ‘ABC’ for n in ‘XYZ’]
[‘AX’, ‘AY’, ‘AZ’, ‘BX’, ‘BY’, ‘BZ’, ‘CX’, ‘CY’, ‘CZ’]
生成器的两种方式
定义:generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误
生成器可以用于for循环和next方法
- 列表生成式改动[] -> ()
g = (x * x for x in range(10))
- 使用yield
斐波拉契数列
1, 1, 2, 3, 5, 8, 13, 21, 34, …
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return 'done'
迭代器
定义:可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator
生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。把list、dict、str等Iterable变成Iterator可以使用iter()函数:
这是因为Python的Iterator对象表示的是一个数据流,
-
Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。
-
可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,
-
所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算
凡是可作用于for循环的对象都是Iterable类型;
凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;
集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。
高阶函数
- map、reduce、filter都是高阶函数,高阶函数的定义为
函数的传入的参数是一个函数名
函数的返回值是一个函数名
- map函数
传入一个func,和array,array中的每个元素都会在fun中运行,结果保存返回新的array - filter也是遍历所有元素,但只保存运行结果为true的
- reduce函数会对array序列中的所有项代入func中运行,并将上一次运行的结果作为x,下一项作为y继续运行,一般用于累积,累和
array = [1,2,3,4,5,6]
value = reduce(lambda x,y:x*y,array,10)
print(value) # value = 10*6*5*4*3*2*1
- sorted用于排序
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
['Zoo', 'Credit', 'bob', 'about']
函数参数
定义默认参数要牢记一点:默认参数必须指向不变对象!
闭包
返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
装饰器
- 不带参数
def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
now = log(now)
- 带参数的,三层嵌套
def log(text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
now = log(‘execute’)(now)
偏函数
>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64
简单总结functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单
面向对象-继承
- 原则1:调用父类函数传入子类实例,父类中的self对象为子类实例
- 原则2:子类实例化会执行__init__但是并不会执行父类的init,这一点与java不同
- 原则3:子类对象可以访问到父类的类属性,但无法访问到实例属性
- 原则4:python2的继承采用深度优先的原则,而python3的调用顺序为广度优先,可以通过mro()或者__mro__【它是通过c3算法生成的列表】,来查看继承的调用的顺序,比如
定义
class A():
def go(self):
print ("go A go!")
def stop(self):
print ("stop A stop!")
def pause(self):
raise Exception("Not Implemented")
class B(A):
def go(self):
super(B, self).go()
print ("go B go!")
class C(A):
def go(self):
super(C, self).go()
print ("go C go!")
def stop(self):
super(C, self).stop()
print ("stop C stop!")
class D(B,C):
def go(self):
super(D, self).go()
print ("go D go!")
def stop(self):
super(D, self).stop()
print ("stop D stop!")
def pause(self):
print ("wait D wait!")
class E(B,C):
pass
执行
a = A()
b = B()
c = C()
d = D()
e = E()
# 说明下列代码的输出结果
a.go()
print('--------')
b.go()
print('--------')
c.go()
print('--------')
d.go()
print('--------')
e.go()
print('--------')
a.stop()
print('--------')
b.stop()
print('--------')
c.stop()
print('--------')
d.stop()
print('--------')
e.stop()
print(D.mro())
打印结果为
go A go!
--------
go A go!
go B go!
--------
go A go!
go C go!
--------
go A go!
go C go!
go B go!
go D go!
--------
go A go!
go C go!
go B go!
--------
stop A stop!
--------
stop A stop!
--------
stop A stop!
stop C stop!
--------
stop A stop!
stop C stop!
stop D stop!
--------
stop A stop!
stop C stop!
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
深度和广度优先的详细介绍https://www.cnblogs.com/zhaof/p/7092400.html
这里详细解释一下:D.mro(),他的打印结果为
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
他的继承顺序为
class D(B,C): # 其中B,C都继承了A
所有根据广度优先的原则,先找到root类,再找出所有的下一级别类,最终顺序为ACBD
那为啥不是ABCD呢?
问得好,我也不知道,我又了下D继承B,C,F
class D(B,C,F): # B,C,F都继承了A
结果打印出来的顺序为:
A-F-C-B-D
结果就很明显了,是按照从右往左排列的
所以结论是:
python3是按照广度继承原则来决定调用顺序的,默认从右到左,不会重复的调用父类
参考博文
https://www.cnblogs.com/xinghuaikang/p/8481712.html
https://www.cnblogs.com/zhaof/p/7092400.html
import的使用
- 在python3中,即使包下没有__init__.py文件,import 包仍然不会报错,而在python2中,包下一定要有该文件,否则import 包报错
- 相对导入时的注意点:
1、文件夹中必须有__init__.py文件,该文件可以为空,但必须存在该文件。
2、不能作为顶层模块来执行该文件夹中的py文件(即不能作为主函数的入口)。就是说我的函数入口文件不能写在这个包里面,因为这是这个包就变成了顶层模块
参考:
python类中的内置属性
1.__slots__属性
用于限制实例的属性
class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
使用后student的实例不会再允许使用student.xxx来添加不在tuple中的属性。
注意点:只对这个类的实例生效,不会对其子类生效
除非在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__。
2.@property 修饰符,将方法变成属性调用
class Student(object):
@property
def score(self):
return self._score
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
3. __str__类似java中的tostring,重写__repr__达到不敲print直接调用对象输出和__str__一样的效果
4.关于迭代
4.1 重写__iter__和__next__将该类实例变成可迭代的对象,可以用于for循环和next方法
class Fib(object):
def __init__(self):
self.a, self.b = 0, 1 # 初始化两个计数器a,b
def __iter__(self):
return self # 实例本身就是迭代对象,故返回自己
def __next__(self):
self.a, self.b = self.b, self.a + self.b # 计算下一个值
if self.a > 100000: # 退出循环的条件
raise StopIteration()
return self.a # 返回下一个值
4.2 重写__getitem__是对象可以像列表一样使用下标取出元素
class Fib(object):
def __getitem__(self, n):
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a
注意:如果要使用切片,getitem中需要另做判断,这里不多赘述,具体可以参考廖雪峰老师的博客
也可以看我以前的博客 https://blog.csdn.net/weixin_42142216/article/details/89363235
4.3 isinstance /issubclass
- isinstance(obj,cls) 检查是否 obj 是否是类 cls 的对象
- issubclass(sub, super) 检查 sub 类是否是 super 类的派生类
描述符的使用
在一般情况下,
一 描述符本身应该定义成新式类, 被代理的类也应该是新式类
二 必须把描述符定义成这个类的类属性,不能为定义到构造函数中
三 要严格遵循该优先级, 优先级由高到底分别是
- 类属性
- 数据描述符
- 实例属性
- 非数据描述符
- 找不到的属性触发__getattr__()
但是,当我们使用实例来调用时,如果这时候同时存在与实例属性同名的类属性或函数,实例属性的优先级大于一切!!!
cell方法
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 call 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
让对象可以让函数一样加(),就是调用的call方法
new 和init
1、继承自object的新式类才有__new__
2、__new__至少要有一个参数cls,代表当前类,此参数在实例化时由Python解释器自动识别
3、__new__必须要有返回值,返回实例化出来的实例,这点在自己实现__new__时要特别注意,可以return父类(通过super(当前类名, cls))__new__出来的实例,或者直接是object的__new__出来的实例
4、__init__有一个参数self,就是这个__new__返回的实例,__init__在__new__的基础上可以完成一些其它初始化的动作,__init__不需要返回值
5、如果__new__传入的cls是自己的类,会自动调用__init__函数,通过return语句里面调用的__new__函数的第一个参数是cls来保证是当前类实例,如果是其他类的类名,;那么实际创建返回的就是其他类的实例,其实就不会调用当前类的__init__函数,也不会调用其他类的__init__函数。
1、在定义子类时没有重新定义__new__()时,Python默认是调用该类的直接父类的__new__()方法来构造该类的实例,如果该类的父类也没有重写__new__(),那么将一直按此规矩追溯至object的__new__()方法,因为object是所有新式类的基类。
2、而如果子类中重写了__new__()方法,那么你可以自由选择任意一个的其他的新式类(必定要是新式类,只有新式类必定都有__new__(),因为所有新式类都是object的后代,而经典类则没有__new__()方法)的__new__()方法来制造实例,包括这个新式类的所有前代类和后代类,只要它们不会造成递归死循环。反正肯定不能调用自己的__new__,这肯定是死循环。
3、对于子类的__init__,其调用规则跟__new__是一致的,当然如果子类和父类的__init__函数都想调用,可以在子类的__init__函数中加入对父类__init__函数的调用。
4、我们在使用时,尽量使用__init__函数,不要去自定义__new__函数,因为这两者在继承派生时的特性还是很不一样的。
总结
1.new是在init之前调用的,init中的self就是new创建处理的实例
2.new必要有一个参数cls(表示当前类),和一个返回值
3.new的返回值可以return父类(通过super(当前类名, cls))__new__出来的实例,或者直接是object的__new__出来的实例
4.在new中不可调用自己的new,这会造成死循环
5.如果子类中没有重写new,python默认调用父类的new
6.只有继承object的新式类,才有new方法
7.new的简单实用是实现单例设计模式
__del方法:析构方法
垃圾回收时触发
__enter , __exit使类支持上下文管理协议
with Foo(‘a.txt’)as f:
先执行Foo的enter,执行完毕后执行exit
- with代码块中的异常会触发Exit方法
- 若exit方法返回True,则该异常会被捕获
实用枚举类定义常量
from enum import Enum, unique
@unique
class Weekday(Enum):
Sun = 0 # Sun的value被设定为0
Mon = 1
Tue = 2
Wed = 3
Thu = 4
Fri = 5
Sat = 6
可以实用Type动态创建类
>>> def fn(self, name='world'): # 先定义函数
... print('Hello, %s.' % name)
...
>>> Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
>>> h = Hello()
>>> h.hello()
Hello, world.
>>> print(type(Hello))
<class 'type'>
>>> print(type(h))
<class '__main__.Hello'>
要创建一个class对象,type()函数依次传入3个参数:
- class的名称;
- 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
- class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。
metaclass用于控制类的行为
https://www.liaoxuefeng.com/wiki/1016959663602400/1017592449371072