1 else用法
- Python中的for、while是在循环体内没有break语句、没有return语句,或者没有异常出现时才执行else语句,try…except…else是在try中无异常产生时执行,注意不包括continue。
- 总的来说,是for、while、try语句中‘没毛病’时才执行,而在if…else语句中是if语句判断为假(有毛病)才执行else,这一点要区分开来
2 魔法方法
python自动产生的(魔法方法):一般形式为 _func_(),python会在对应的时机自动调用该函数;
- 当我们调用 x = SomeClass() 的时候, _init_ 并不是第一个被调用的方法。事实上,第一个被调用的是 _new_ ,这个 方法才真正地创建了实例。当这个对象的生命周期结束的时候, _del_ 会被调用。
- __new__是用来创建类并返回这个类的实例, 而__init__只是将传入的参数来初始化该实例.
- __new__在创建一个实例的过程中必定会被调用,但_init__就不一定,比如通过pickle.load的方式反序列化一个实例时就不会调用_init。
- __new__方法总是需要返回该类的一个实例,而__init__不能返回除了None的任何值
-
_new_(cls,[…)
_new_ 是对象实例化时第一个调用的方法,它只取下 cls 参数,并把其他参数传给 _init_ 。 _new_ 很少使用,但是也有它适合的场景,尤其是当类继承自一个像元组或者字符串这样不经常改变的类型的时候。我不打算深入讨论 _new_ ,因为它并不是很有用, Python文档 中 有详细的说明。 -
_init_(self,[…])
类的初始化方法。它获取任何传给构造器的参数(比如我们调用 x = SomeClass(10, ‘foo’) , _init_ 就会接到参数 10 和 ‘foo’ 。 _init_ 在Python的类定义中用的最多。 -
_del_(self)
_new_ 和 _init_ 是对象的构造器, _del_ 是对象的销毁器。它并非实现了语句 del x (因此该语句不等同于 x._del_())。而是定义了当对象被垃圾回收时的行为。 当对象需要在销毁时做一些处理的时候这个方法很有用,比如 socket 对象、文件对象。但是需要注意的是,当Python解释器退出但对象仍然存活的时候, _del_ 并不会 执行。 所以养成一个手工清理的好习惯是很重要的,比如及时关闭连接。
-
3 super()
- super() 函数是用于调用父类(超类)的一个方法。
- super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。
- MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表。
- super(type[, object-or-type]) type – 类 object-or-type – 类,一般是 self
- super函数超级之处在于你不需要明确给我任何基类的名字,它会自动帮你找出所有基类以及对应的方法。由于你不用给出基类的名字,这就意味着如果需要改变类继承关系,你只要改变class语句里的父类即可,而不必在大量代码中去修改所有被继承的方法。
4 钻石继承
- 如果子类继承自两个单独的超类,而那两个超类又继承自同一个公共基类,那么就构成了钻石继承体- 系。这种继承体系很像竖立的菱形,也称作菱形继承
- 调用过程中,base会被重置多次,所以需要用super()解决。
- 具体操作看参考链接
5 类与对象的继承和组合
- 组合和继承是面向对象中两种代码复用的方式。
- 把类的对象实例化放到一个新的类里面叫做类的组合,组合就是指几个横向关系的类放在一起,纵向关系的类放在一起是继承,根据实际应用场景确定。简单的说,组合用于“有一个”的场景中,继承用于“是一个”的场景中
- 在新类里面创建原有类的对象,重复利用已有类的功能。(has-a关系)
- 可以使用现有类的功能,并且在无需重复编写原有类的情况下对原有类进行功能上的扩展。(is-a关系)
- 当你这个类定义完的时候,类定义就变成类对象,可以直接通过“类名.属性”或者“类名.方法名()”引用或使用相关的属性或方法。
- 一般不要试图定义出类的所有特性和方法,应该利用继承和组合的机制来进行扩展
- 我们还可以利用不同的词性来进行命名,对于属性一般是用名词,对于方法名一般用动词。
- 除非两个类之间是“is-a”的关系,否则不要轻易的使用继承,不要单纯的为了实现代码的重用而使用继承,因为过多的使用继承会破坏代码的可维护性,当父类被修改时,会影响到所有继承自它的子类,从而增加程序的维护难度和成本。
- 采用接口与组合的方式比采用继承的方式具有更好的可扩展性。
6 对象的属性和方法名相同
如果对象的属性和方法名相同,属性会覆盖方法。
所以不要把类属性和方法名重复,这样会导致出现不易发现的BUG
7 工厂函数
- 工厂函数顾名思义就是一个能产生函数的工厂,其目的是对一个需要输入多个参数的函数分类封装,不同使用者只需要输入更少的参数或单个参数就能调用。
- 工厂函数都是类对象, 即当你调用他们时, 创建的其实是一个类实例
- Python 2.2 统一了类型和类, 所有的内建类型现在也都是类, 在这基础之上, 原来的所谓内建转换函数象 int(), type(), list() 等等, 现在都成了工厂函数。 也就是说虽然他们看上去有点象函数, 实质上他们是类。当你调用它们时, 实际上是生成了该类型的一个实例, 就象工厂生产货物一样。
8 反射操作符
所有反射运算符魔法方法和它们的常见版本做的工作相同,只不过是处理交换连个操作数之后的情况。绝大多数情况下,反射运算和正常顺序产生的结果是相同的,所以很可能你定义 _radd_ 时只是调用一下 _add_。注意一点,操作符左侧的对象(也就是上面的 other )一定不要定义(或者产生 NotImplemented 异常) 操作符的非反射版本。例如,在上面的例子中,只有当 other 没有定义 _add_ 时 some_object._radd_ 才会被调用。
9 静态属性 类方法 静态方法
静态属性
- 静态属性:把方法变成像属性一样调用
- 在类的函数属性前加@property可以让该函数以数据属性的方式调用.
注意:
- 静态属性不可传参数,(只有self)
- 类的实例默认无法修改静态属性
- 如果想对实例修改,调用静态属性做限制,可以用setter, getter, deleter,等装饰器.
class Test:
def __init__(self, a, b):
self.a = a
self.b = b
@property
def cal1(self):
return self.a + self.b
@cal1.setter # 利用此方法设置静态属性
def cal1(self, value):
print('hahaha', self, value)
self.__dict__['cal1'] = value # 直接操作字典,避免无限递归
@cal1.getter # 调用属性时触发,触发条件只与实例有关
def cal1(self):
print('get is running')
return self.a + self.b
@cal1.deleter
def cal1(self): # 删除静态属性时触发
print('deleter is running')
self.__dict__.pop('cal1')
t = Test('love', 'you')
print(t.cal1)
print(Test.cal1)
t.cal1 = 'sa' # 而实例不可设置静态属性,除非有setter
#Test.cal1 = 'happy' # 类可以设置静态属性
print(t.__dict__)
del t.cal1
print(t.__dict__)
#result
get is running
loveyou
<property object at 0x000001AD05B61B88>
hahaha <__main__.Test object at 0x000001AD05B6E0F0> sa
{'a': 'love', 'b': 'you', 'cal1': 'sa'}
deleter is running
{'a': 'love', 'b': 'you'}
类方法
类方法:不用实例化就可以执行类的方法
@classmethod
但是我如果只想执行类的方法,不想跟任何实例有捆绑,该怎么做呢,python给我们提供了classmethod这个装饰器,只要用了这个方法, 就表示这个装饰器下面的方法是专门给类用的
这个方法就是只是类在调用类的方法,跟实例没关系,只要加了classmethod就只能供类调用
静态方法
- 静态方法:名义上的归属类管理,类的工具包, 不能使用类变量和实例变量
- 叫类的工具包,不跟类绑定也不跟实例绑定
- staticmethod 用类可以调用静态方法,实例也可以调用静态方法
- 不跟类和实例绑定,只叫类的工具包
10 描述符 (descriptor)
-
_dict_ (每个对象均具备该属性)
- 字典类型,存放本对象的属性,key(键)即为属性名,value(值)即为属性的值,形式为{attr_key : attr_value}
-
某个类,只要是内部定义了方法 get, set, delete 中的一个或多个,就可以称为描述符
-
对象属性的访问顺序:
- 实例属性
- 类属性
- 父类属性
- _getattr_()方法
魔法方法:get(), set(), delete()
- 方法的原型为:
- _get_(self, instance, owner)
- _set_(self, instance, value)、
- _del_(self, instance)
11 迭代器和生成器
迭代器:
- 迭代器是一个可以记住遍历的位置的对象。
- 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
- 迭代器有两个基本的方法:iter() 和 next()。
- 字符串,列表或元组对象都可用于创建迭代器:
- 迭代器对象可以使用常规for语句进行遍历:
- 也可以使用 next() 函数:
- 把一个类作为一个迭代器使用需要在类中实现两个方法 _iter_() 与 _next_() 。
- _iter_() 方法返回一个特殊的迭代器对象, 这个迭代器对象实现了 _next_() 方法并通过 StopIteration 异常标识迭代的完成。
- StopIteration 异常用于标识迭代的完成,防止出现无限循环的情况,在 _next_() 方法中我们可以设置在完成指定循环次数后触发 StopIteration 异常来结束迭代。
生成器:
- 使用了yield的函数被称为生成器(generator)
- 生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。
- 在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield的值, 并在下一次执行 next() 方法时从当前位置继续运行。
- 调用一个生成器函数,返回的是一个迭代器对象。
12 文件&文件夹的读取/写入
文件模式
- ‘r’ – 只读模式。默认值。它只允许您读取文件,而不允许您修改它。当使用此模式时,文件必须存在。
- ‘w’ – 写入模式。 如果它不存在 ,将创建一个新文件,否则将覆盖文件,并允许您写入它。
- ‘a’ – 追加模式。它将把数据写到文件的末尾。它不会擦除文件,并且在这种模式下,文件必须已经存在。
- ‘rb’ – 二进制读模式。这类似于r,除了读取是在二进制模式下强制执行的。
- ‘r+’ – 读写模式。这允许您同时读写文件,而不必使用r和w ,但前提是文件必须已存在
- ‘rb+’ – 二进制读写模式。和r+一样,只是数据是二进制的
- ‘wb’ – 二进制写入模式。和w一样,只是数据是二进制的。
- ‘w+’ – 读写模式,功能与r+相同,但如果文件不存在,则创建一个新文件。否则,文件将被覆盖
- ‘wb+’ – 二进制模式下的读写模式。和w+一样,但是数据是二进制的。
- ‘ab’ – 以二进制模式附加。类似于a,只是数据是二进制的。
- ‘a+’ – 附加和读取模式。类似于w+,因为如果文件不存在,它将创建一个新文件 , 如果文件存在,则文件指针位于文件的末尾。
- ‘ab+’ – 二进制附加和读取模式。与a+相同,只是数据是二进制的。
Python 3添加了一个新的模式来进行独占创建,这样您就不会意外地截断或覆盖现有文件。
- ‘x’ – 以独占模式打开,如果文件已经存在,将引发FileExistsError
- ‘xb’ – 同x模式,只是数据是二进制
- ‘x+’ – 读写模式。类似于w+,因为如果文件不存在,它将创建一个新文件。否则将产生 FileExistsError.
- ‘xb+’ – 和x+完全一样,数据是二进制的
逐行读取文件
- 直接for语句
#逐行读取
with open('myfile.txt', 'r') as fp:
for line in fp:
print(line)
- readline()允许对逐行迭代进行更细粒度的控制。
#逐行读取
with open('myfile.txt', 'r') as fp:
content='';
while True:
content=fp.readline();
print(content)
if(content==''):
#如果读到空字符串,证明结束并返回
break;
但是,不建议将for循环迭代器和readline()一起使用
用readlines()函数存储文件行的可迭代集合 :
#创建测试文件(当前项目路径下)
with open('myfile.txt', 'w') as fp:
fp.write('我\n爱\n你')
#逐行读取
with open("myfile.txt", "r") as fp:
lines = fp.readlines()
#查询
for i in range(len(lines)):
print("Line " + str(i) + ": " + lines[i])
遍历文件
- 要迭代所有文件,包括子目录中的文件,请使用os.walk :
import os
for root, folders, files in os.walk('.'): #该路径为云环境上项目路径,可以替换成其它路径甚至根路径'/'
# 获取所有文件夹
for file in folders:
print(root,file)
#获取所有文件
for filename in files:
print(root, filename)
获取文件完整内容
- 文件I / O的首选方法是使用with关键字;这样可以确保一旦阅读或写作完成后,python能够帮你处理剩余的文件关闭、资源清理等工作
- 注意不要使用os.linesep作为行结束符;写入时换行请使用\ n
- 如果要指定编码,只需将编码参数添加到open函数即可
with open('my_file.txt', 'w', encoding='utf-8') as f:
f.write('utf-8 text')
- 也可以使用print语句写入文件。在python2和python3中,机制是不同的,但是概念是相同的,你可以把本来会出现在屏幕上的输出结果发送到一个文件中
检查文件或路径是否存在
- 用try/except异常捕获检查:
其他模块看超链接
13 Python之多态和鸭子类型
多态:
- 在Python中一切皆对象,处处是多态。
- 多态指的是同一种事物的多种形态,在程序中用继承可以表现出多态。
- 多态性:可以在不考虑对象具体类的形况下直接参考基类的标准使用对象。
在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用的。"鸭子类型"像多态一样工作,但是没有继承。
比如:一个对象中只要有__iter__方法,那么这个对象就是可迭代对象;一个对象只要含有__iter__方法和__next__方法,那么这个对象就是迭代器;而不用去继承什么,证明什么,只要你符合我的标准,你就是我想要的!
14 Python内置数据类型时间复杂度
- list
- 以完全随机的列表考虑平均情况。
- 列表是以数组(Array)实现的。
- 如果需要在一个队列的两端进行增删的操作,应当使用collections.deque(双向队列)
- 双向队列(collections.deque)
deque (double-ended queue,双向队列)是以双向链表的形式实现的 (Well, a list of arrays rather than objects, for greater efficiency)。双向队列的两端都是可达的,但从查找队列中间的元素较为缓慢,增删元素就更慢了。
- 集合(set)
- 字典(dict)
下列字典的平均情况基于以下假设:- 对象的散列函数足够撸棒(robust),不会发生冲突。
- 字典的键是从所有可能的键的集合中随机选择的。