二、彩蛋
1、python之禅
import this
《python之禅》,作者蒂姆·彼得斯优美总比丑陋好。(编程也是一门艺术,也以美为追求)明了优于含蓄。(代码表现的是逻辑,应当清晰有条理)简单胜于复杂。(简单的代码易于被人理解,代码是给人看的,被机器执行的)复杂总比杂乱无章好。(就算复杂也需要有条不紊)扁平比嵌套好。稀疏比密集好。可读性很重要。不能以特例的实用性来打破这些规则。错误不应该悄无声息地过去。除非你确定需要这样做。面对模棱两可,最好不要盲目去猜测。应该有一种————最好只有一种————显而易见解决方案。尽管这种方式一开始可能不太明显,因为你不是Python之父(这里的Dutch是指Guido)。现在开始做总比没有行动强。但做之前要三思而行。如果实现方案很难解释,那就不是一个好方案。如果实现方案很容易解释,那么它可能是个好方案。命名空间是一个伟大的想法——让我们更多的使用起来吧!
二、类与类的关系
• is-a 继承
继承是指一个类(称为子类、子接口)继承另外一个类(称为父类、父接口)的功能,并可以增加它自己的新功能的能力。
• has-a 关联/聚合/合成
关联体现的是两个类之间语义级别的一种强依赖关系,比如我和我的朋友, 这种关系比依赖更强、不存在依赖关系的偶然性、关系也不是临时性的, 一般是长期性的,而且双方的关系一般是平等的。关联可以是单向、双向的。 表现在代码层面,为被关联类B以类的属性形式出现在关联类A中, 也可能是关联类A引用了一个类型为被关联类B的全局变量。
聚合是关联关系的一种特例,它体现的是整体与部分的关系,即has-a的关系。此时整体与部分之间是可分离的,它们可以具有各自的生命周期,部分可以属于多个整体对象,也可以为多个整体对象共享。比如计算机与CPU、公司与员工的关系等,比如一个航母编队包括海空母舰、驱护舰艇、舰载飞机及核动力攻击潜艇等。表现在代码层面,和关联关系是一致的,只能从语义级别来区分。组合也是关联关系的一种特例,它体现的是一种contains-a的关系,这种关系比聚合更强,也称为强聚合。它同样体现整体与部分间的关系,但此时整体与部分是不可分的,整体的生命周期结束也就意味着部分的生命周期结束,比如人和人的大脑。表现在代码层面,和关联关系是一致的,只能从语义级别来区分。
• use-a 依赖
简单的理解,依赖就是一个类A使用到了另一个类B,而这种使用关系是具有偶然性的、临时性的、非常弱的,但是类B的变化会影响到类A。比如某人要过河,需要借用一条船,此时人与船之间的关系就是依赖。表现在代码层面,为类B作为参数被类A在某个method方法中使用。
类与类关系的强弱程度依次为:组合>聚合>关联>依赖。
2、python漫画
import antigravity
3、查看帮助
#查看关键字help("keywords")
三、字典高级用法
1、字典推导式
list1 = ['a','b','c']dict1 = {k:i for i,k in enumerate(list1) }print(dict1)
结果为:{'a': 0, 'b': 1, 'c': 2}
2、字典按值排序
方式1:
d = {'a': 1,'b': 6,'c': 55,'d': 1,'f': 0}sort = sorted(zip(d.values(),d.keys()))sort_d = {v:k for k,v in sort}print(sort_d)
结果为:{'f': 0, 'a': 1, 'd': 1, 'b': 6, 'c': 55}
方式2:
d = {'a': 1,'b': 6,'c': 55,'d': 1,'f': 0}print({k:d[k] for k in sorted(d,key=d.get)})
结果为:{'f': 0, 'a': 1, 'd': 1, 'b': 6, 'c': 55}
方式3:python
d = {'a': 1,'b': 6,'c': 55,'d': 1,'f': 0}print(d.items())print(dict(sorted(d.items(),key=lambda v:v[1])))
结果为:dict_items([('a', 1), ('b', 6), ('c', 55), ('d', 1), ('f', 0)]){'f': 0, 'a': 1, 'd': 1, 'b': 6, 'c': 55}
方式4:
from operator import itemgetterd = {'a': 1,'b': 6,'c': 55,'d': 1,'f': 0}print( dict(sorted(d.items(),key=itemgetter(1))))
结果为:dict_items([('a', 1), ('b', 6), ('c', 55), ('d', 1), ('f', 0)]){'f': 0, 'a': 1, 'd': 1, 'b': 6, 'c': 55}
3、字典值过滤
dict1 = {'a': 1,'b': 6,'c': 55,'d': 1,'f': 0}# 方式1dict1_gt1 = {key: value for key, value in dict1.items() if value > 1}print(dict1_gt1)# 方式2dict1_gt2 = {x:dict1[x] for x in filter(lambda x:dict1[x] > 1,dict1)}print(dict1_gt2)
结果为:{'b': 6, 'c': 55}{'b': 6, 'c': 55}
4、get()方法
d = {'a':1,'b':2}print(d.get('c',3))print(d.get('a'))print(d.get('d',None))
结果为:31None
get()方法可以获取给定的键名的值,如果键名不存在,最好给定一个默认值为None作为返回值。
5、合并字典
方式1:
d1 = {'hello':1}d2 = {'world':2}print({**d1,**d2})
结果为:{'hello': 1, 'world': 2}
方式2:
d1 = {'hello':1}d2 = {'world':2}print(dict(d1.items()| d2.items()))
结果为:{'hello': 1, 'world': 2}
方式3:
d1 = {'hello':1}d2 = {'world':2}d1.update(d2)print(d1)
结果为:{'hello': 1, 'world': 2}
四、List高级用法
1、查找最大或最小的N个元素
使用堆区的大根堆与小根堆来实现。
大根堆:数据以类似树的结构把按大到小排序;
小根堆:数据以类似树的结构把按小到大排序.
import heapqlist1 = [34, 25, 12, 99, 87, 63, 58, 78, 88, 92]list2 = [ {'name': 'IBM', 'shares': 100, 'price': 91.1}, {'name': 'AAPL', 'shares': 50, 'price': 543.22}, {'name': 'FB', 'shares': 200, 'price': 21.09}, {'name': 'HPQ', 'shares': 35, 'price': 31.75}, {'name': 'YHOO', 'shares': 45, 'price': 16.35}, {'name': 'ACME', 'shares': 75, 'price': 115.65} ]# 列表取前5个最大的值print(heapq.nlargest(5, list1))# 列表取前5个最小的值print(heapq.nsmallest(3, list1))# 元素按'price'排序取前2个最大的值print(heapq.nlargest(2, list2, key=lambda x: x['price']))print(heapq.nlargest(2, list2, key=lambda x: x['shares']))
结果为:[99, 92, 88, 87, 78][12, 25, 34][{'name': 'AAPL', 'shares': 50, 'price': 543.22}, {'name': 'ACME', 'shares': 75, 'price': 115.65}][{'name': 'FB', 'shares': 200, 'price': 21.09}, {'name': 'IBM', 'shares': 100, 'price': 91.1}]
2、查找列表中元素出现频率较多的前三个元素
import randomfrom collections import Counterlist1 = [random.randint(1,10) for i in range(20)]counter = Counter(list1)print(list1)# 查找出现频率最多的前3个元素print(counter.most_common(3))
结果为:[10, 8, 7, 2, 10, 7, 6, 9, 1, 10, 7, 3, 3, 10, 8, 10, 4, 6, 8, 10][(10, 6), (8, 3), (7, 3)]
10出现6次,8和7各出现3次。
3、函数Counter的使用
from collections import Counterstr1 = 'hello world'# 字符串翻转str2 = ''.join([i for i in reversed(str1)])print(str2)print(Counter(str1))print(Counter(str2))# 字符串组成成分是否一样print(Counter(str1) == Counter(str2))
结果为dlrow ollehCounter({'l': 3, 'o': 2, 'h': 1, 'e': 1, ' ': 1, 'w': 1, 'r': 1, 'd': 1})Counter({'l': 3, 'o': 2, 'd': 1, 'r': 1, 'w': 1, ' ': 1, 'e': 1, 'h': 1})True
说明str1和str2是由相同字母组成的(顺序可能不同)的字符串。
4、查找频率出现最高的元素
import random#查找列表中频率最高的值list1 = [random.randint(1,10) for i in range(20)]print(list1)print(max(set(list1),key=list1.count))
结果为:[4, 5, 2, 3, 3, 5, 5, 1, 2, 4, 1, 3, 7, 6, 5, 9, 8, 8, 3, 3]3
5、二维数组转置
'''二维数组转置1 23 45 61 3 52 4 6'''str1 = [[1,2],[3,4],[5,6]]print(*str1)print(list(zip(*str1)))
结果为:[1, 2] [3, 4] [5, 6][(1, 3, 5), (2, 4, 6)]
6、通过map()方法把元素连接成字符串
items = ['hello','world']print(' '.join(items))numbers = [1,2,3,4]print(' '.join(map(str,numbers)))data = [1,True,'hello','world']print(' '.join(map(str,data)))
结果为:hello world1 2 3 41 True hello world
7、获取列表最大值最小值的索引
items = [1,45,23,34,23,89,0]print(items.__getitem__(2))print(min(range(len(items)),key=items.__getitem__))print(max(range(len(items)),key=items.__getitem__))
结果为:2365
items.__getitem__(2)的作用是通过索引获取对应的元素。
五、元类
• 元数据 - 描述数据的数据
• 元类 - 描述类的类
元类是不能直接创建实例的,否则会报错,错误信息为:
Can't instantiate abstract class A with abstract methods salary
# 导入创建元类的包from abc import ABCMeta, abstractmethod# 元数据 - 描述数据的数据# 元类 - 描述类的类class Employee(metaclass=ABCMeta): __slots__ = ('name',) def __init__(self, name): self.name = name @property @abstractmethod def salary(self): passclass Manager(Employee): __slots__ = ('name',) @property def salary(self): return 15000class Programmer(Employee): __slots__ = ('name', '_working_hour') def __init__(self, name): super(Programmer, self).__init__(name) self._working_hour = 0 @property def working_hour(self): return self._working_hour @working_hour.setter def working_hour(self, working_hour): self._working_hour = working_hour \ if working_hour > 0 else 0 @property def salary(self): return 200 * self.working_hourclass Salesman(Employee): __slots__ = ('name', 'sales') def __init__(self, name): super(Salesman, self).__init__(name) self.sales = 0 @property def salary(self): return 1800 + self.sales * 0.05def main(): emps = [ Manager('刘备'), Programmer('诸葛亮'), Programmer('关羽'), Salesman('张飞'), Salesman('马超'), Programmer('黄忠') ] for emp in emps: # 有了__slots__限制,所以不能添加不存在的属性 # emp.gender = 'Male' if isinstance(emp, Programmer): hour = int(input(f'请输入{emp.name}本月工作时间: ')) emp.working_hour = hour elif isinstance(emp, Salesman): sales = float(input(f'请输入{emp.name}本月销售额: ')) emp.sales = sales print('%s月薪为: %.2f元' % (emp.name, emp.salary))if __name__ == '__main__': main()
• @property
把方法属性化,在调用方法时可以不用括号,可以直接赋值。
• @abstractmethod
把方法设置成抽象方法,具体功能由子类实现。
•_working_hour
受保护的属性,不能直接访问,要通过setter装饰器来访问
@property def working_hour(self): return self._working_hour @working_hour.setter def working_hour(self, working_hour): self._working_hour = working_hour \ if working_hour > 0 else 0
• __slots__
限制属性数量,可以起到压缩内存的作用。
六、多重继承
class A: def foo(self): print('foo() in A')class B(A): def foo(self): print('foo() in B')class C(A): def foo(self): print('foo() in C')class D(C, B): passdef main(): print(D.__mro__) obj = D() obj.foo() # 判断实例obj是否属于C类 print(isinstance(obj,C))if __name__ == '__main__': main()
结果为:(,,,,)foo() in BTrue
• __mro__
查看类中的属性和方法继承顺序。
python2和python3使用的继承顺序算法不一样.python2用的是类似深度算法,第一个父类找不到属性或方法,就会去第一个父类的分类中查找,直到找到为止,找不到就会报错。python3用的是类似广度算法,第一个父类找不到,就会去第二个父类中查找。
七、生成器和迭代器
1.可迭代对象
但凡内置有iter方法的对象,都称为可迭代对象。可迭代的对象 如:str,list,tuple,dict,set,文件对象.。
2、迭代器对象
1既内置又next方法的对象,执行该方法可以不依赖索引取值 2.又内置有iter方法的对象,执行迭代器的iter方法得到的依然是迭代器本身
迭代器一定是可迭代对象,可迭代对象不一定是迭代器对象,文件对象本身就是一个迭代器对象.
3、for循环本质为迭代器循环
工作原理: 1.先调用in后对象的iter方法,将其变成一个迭代器对象 2.调用next(迭代器),将得到的返回值赋值给变量名 3.循环往复直到next(迭代器)抛出异常,for会自动捕捉异常然后结束循环
ps:可以从for的角度,分辨但凡可以被for循环取值的对象就是可迭代对象
4、迭代器优点
1.提供了一种通用不依赖索引的迭代取值方式 2.同一时刻在内存中只存在一个值,更节省内存
5、迭代器缺点
1.取值不如按照索引的方式灵活,不能取指定的某一个值,只能往后取,不能往前取 2.无法预测迭代器的长度
6、生成器
生成器就是一种自定义的迭代器,本质为迭代器 但凡函数内包含yield关键字,调用函数不会执行函数体代码,会得到一个返回值,该返回值就是生成器对象。yield只能在函数内使用 1.yield提供了一种自定义迭代器的解决方案 2.yield可以保存函数的暂停的状态 3.yield对比return:相同点,都可以返回值,值得类型与个数没有限制,不同点:yield可以返回多次值,而return只能返回一次值函数就会结束
"""生成器和迭代器"""# 用循环的方式实现斐波那契数列求解def fib(n): a, b = 0, 1 for _ in range(n): a, b = b, a + b return a# 用生成器的方式实现斐波那契数列求解def fib2(n): a, b = 0, 1 for _ in range(n): a, b = b, a + b yield a# 重写__iter__和__next__方法实现生成器class Fib3: def __init__(self, n): self.n = n self.a, self.b = 0, 1 self.idx = 0 def __iter__(self): return self def __next__(self): if self.idx << span=""> self.n: self.a, self.b = self.b, self.a + self.b self.idx += 1 return self.a raise StopIteration()def main(): print(tuple(Fib3(20))) fib3 = Fib3(20) for val in fib3: print(val) print(fib) print(fib(20)) gen = fib2(20) print(gen) for val in gen: print(val) # 生成式 gen2 = (x ** 3 for x in range(1, 11)) print(gen2) for val in gen2: print(val)if __name__ == '__main__': main()
省空间:生成器
省时间:生成式
一般优化是节省时间:使用缓存。
八、混入
class SetOnceMappingMixin: __slots__ = () def __setitem__(self, key, value): if key in self: raise KeyError(str(key) + ' already set') return super().__setitem__(key, value)class SetOnceDict(SetOnceMappingMixin, dict): passdef main(): dict1 = SetOnceDict() try: # 已经存在的key中的值保持不变 dict1['username'] = 'jackfrued' dict1['username'] = 'hellokitty' dict1['username'] = 'wangdachui' except KeyError: pass print(dict1)if __name__ == '__main__': main()
结果为:{'username': 'jackfrued'}
python的mixin可以实现程序在运行的过程中,动态修改一个类的继承关系。
九、git
查看分支git branch创建分支git branch develop 切换分支git checkout develop创建并切换分支git checkout -b develop创建并切换分支git checkout - b issue123切换分支git checkout develop删除分支git branch -d issue123所有文件放到暂存区git add .查看暂存区状态git status提交git commit -m '提交信息'查看日志git log文件还没放到暂存区时的文件回退git checkout -- 文件提交文件后想回到其它版本git reset --hard 文件哈希id 或者版本号git checkout master合并git merge --no-ff develop强行删除还没合并的分支git branch D推送到服务器git push拉取git pull
如果觉得文章有用,点个“在看”呗!