原文地址:https://blog.51cto.com/xvjunjie/2156525
No.1 一切皆对象
众所周知,Java中强调“一切皆对象”,但是Python中的面向对象比Java更加彻底,因为Python中的类(class)也是对象,函数(function)也是对象,而且Python的代码和模块也都是对象。
- Python中函数和类可以赋值给一个变量
- Python中函数和类可以存放到集合对象中
- Python中函数和类可以作为一个函数的参数传递给函数
- Python中函数和类可以作为返回值
Step.1
# 首先创建一个函数和一个Python3.x的新式类
class Demo(object): def __init__(self): print("Demo Class")
# 定义一个函数
def function(): print("function")
# 在Python无论是函数,还是类,都是对象,他们可以赋值给一个变量
class_value = Demo
func_value = function
# 并且可以通过变量调用
class_value() # Demo Class
func_value() # function
Step.2
# 将函数和类添加到集合中
obj_list = []
obj_list.append(Demo)
obj_list.append(function)
# 遍历列表
for i in obj_list:
print(i)
# #
Step.3
# 定义一个具体函数
def test_func(class_name, func_name): class_name() func_name()
# 将类名和函数名传入形参列表
test_func(Demo, function)
# Demo Class
# function
Step.4
# 定义函数实现返回类和函数
def test_func2(): return Demo def test_func3(): return function
# 执行函数
test_func2()() # Demo Class
test_func3()() # function
No.2 关键字type、object、class之间的关系
在Python中,object
的实例是type
,object
是顶层类,没有基类;type
的实例是type
,type
的基类是object
。Python中的内置类型的基类是object
,但是他们都是由type
实例化而来,具体的值由内置类型实例化而来。在Python2.x的语法中用户自定义的类没有明确指定基类就默认是没有基类,在Python3.x的语法中,指定基类为object
。
# object是谁实例化的?
print(type(object)) #
# object继承自哪个类?
print(object.__bases__) # ()
# type是谁实例化的? print(type(type)) # # type继承自哪个类? print(type.__bases__) # (,) # 定义一个变量 value = 100 # 100由谁实例化? print(type(value)) # # int由谁实例化? print(type(int)) # # int继承自哪个类? print(int.__bases__) # (,)
# Python 2.x的旧式类
class OldClass(): pass # Python 3.x的新式类 class NewClass(object): pass
No.3 Python的内置类型
在Python中,对象有3个特征属性:
- 在内存中的地址,使用
id()
函数进行查看 - 对象的类型
- 对象的默认值
Step.1 None类型
在Python解释器启动时,会创建一个None类型的None对象,并且None对象全局只有一个。
Step.2 数值类型
- ini类型
- float类型
- complex类型
- bool类型
Step.3 迭代类型
在Python中,迭代类型可以使用循环来进行遍历。
Step.4 序列类型
- list
- tuple
- str
- array
- range
- bytes, bytearray, memoryvie(二进制序列)
Step.5 映射类型
- dict
Step.6 集合类型
- set
- frozenset
Step.7 上下文管理类型
- with语句
Step.8 其他类型
- 模块
- class
- 实例
- 函数
- 方法
- 代码
- object对象
- type对象
- ellipsis(省略号)
- notimplemented
NO.4 魔法函数
Python中的魔法函数使用双下划线开始,以双下划线结尾。关于详细介绍请看我的文章——《全面总结Python中的魔法函数》。
No.5 鸭子类型与白鹅类型
鸭子类型是程序设计中的推断风格,在鸭子类型中关注对象如何使用而不是类型本身。鸭子类型像多态一样工作但是没有继承。鸭子类型的概念来自于:“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”
# 定义狗类
class Dog(object): def eat(self): print("dog is eatting...") # 定义猫类 class Cat(object): def eat(self): print("cat is eatting...") # 定义鸭子类 class Duck(object): def eat(self): print("duck is eatting...") # 以上Python中多态的体现 # 定义动物列表 an_li = [] # 将动物添加到列表 an_li.append(Dog) an_li.append(Cat) an_li.append(Duck) # 依次调用每个动物的eat()方法 for i in an_li: i().eat() # dog is eatting... # cat is eatting... # duck is eatting...
白鹅类型是指只要 cls
是抽象基类,即 cls
的元类是 abc.ABCMeta
,就可以使用 isinstance(obj, cls)
。
No.6 协议、 抽象基类、abc模块和序列之间的继承关系
- 协议:Python中的非正式接口,是允许Python实现多态的方式,协议是非正式的,不具备强制性,由约定和文档定义。
- 接口:泛指实体把自己提供给外界的一种抽象化物(可以为另一实体),用以由内部操作分离出外部沟通方法,使其能被内部修改而不影响外界其他实体与其交互的方式。
我们可以使用猴子补丁
来实现协议,那么什么是猴子补丁呢?
猴子补丁就是在运行时修改模块或类,不去修改源代码,从而实现目标协议接口操作,这就是所谓的打猴子补丁。
Tips:猴子补丁的叫法起源于Zope框架,开发人员在修改Zope的Bug时,经常在程序后面追加更新的部分,这些
杂牌军补丁
的英文名字叫做guerilla patch
,后来写成gorllia
,接着就变成了monkey
。
猴子补丁的主要作用是:
- 在运行时替换方法、属性
- 在不修改源代码的情况下对程序本身添加之前没有的功能
- 在运行时对象中添加补丁,而不是在磁盘中的源代码上
应用案例:假设写了一个很大的项目,处处使用了json模块来解析json文件,但是后来发现ujson比json性能更高,修改源代码是要修改很多处的,所以只需要在程序入口加入:
import json
# pip install ujson
import ujson
def monkey_patch_json(): json.__name__ = 'ujson' json.dumps = ujson.dumps json.loads = ujson.loads monkey_patch_json()
Python 的抽象基类有一个重要实用优势:可以使用 register
类方法在终端用户的代码中把某个类 “声明” 为一个抽象基类的 “虚拟” 子 类(为此,被注册的类必腨满足抽象其类对方法名称和签名的要求,最重要的是要满足底 层语义契约;但是,开发那个类时不用了解抽象基类,更不用继承抽象基类 。有时,为了让抽象类识别子类,甚至不用注册。要抑制住创建抽象基类的冲动。滥用抽象基类会造成灾难性后果,表明语言太注重表面形式 。
- 抽象基类不能被实例化(不能创建对象),通常是作为基类供子类继承,子类中重写虚函数,实现具体的接口。
- 判定某个对象的类型
- 强制子类必须实现某些方法
抽象基类的定义与使用
import abc
# 定义缓存类
class Cache(metaclass=abc.ABCMeta): @abc.abstractmethod def get(self, key): pass @abc.abstractmethod def set(self, key, value): pass # 定义redis缓存类实现Cache类中的get()和set()方法 class RedisCache(Cache): def set(self, key): pass def get(self, key, value): pass
值得注意的是:Python 3.0-Python3.3之间,继承抽象基类的语法是class ClassName(metaclass=adc.ABCMeta)
,其他版本是:class ClassName(abc.ABC)
。
- collections.abc模块中各个抽象基类的UML类图
No.7 isinstence和type的区别
class A(object):
pass class B(A): pass b = B() print(isinstance(b, B)) print(isinstance(b, A)) print(type(b) is B) print(type(b) is A) # True # True # True # False
No.8 类变量和实例变量
- 实例变量只能通过类的实例进行调用
- 修改模板对象创建的对象的属性,模板对象的属性不会改变
- 修改模板对象的属性,由模板对象创建的对象的属性会改变
# 此处的类也是模板对象,Python中一切皆对象
class A(object): #类变量 number = 12 def __init__(self): # 实例变量 self.number_2 = 13 # 实例变量只能通过类的实例进行调用 print(A.number) # 12 print(A().number) # 12 print(A().number_2) # 13 # 修改模板对象创建的对象的属性,模板对象的属性不会改变 a = A() a.number = 18 print(a.number) # 18 print(A().number) # 12 print(A.number) # 12 # 修改模板对象的属性,由模板对象创建的对象的属性会改变 A.number = 19 print(A.number) # 19 print(A().number) # 19
No.9 类和实例属性以及方法的查找顺序
- 在Python 2.2之前只有经典类,到Python2.7还会兼容经典类,Python3.x以后只使用新式类,Python之前版本也会兼容新式类
- Python 2.2 及其之前类没有基类,Python新式类需要显式继承自
object
,即使不显式继承也会默认继承自object
- 经典类在类多重继承的时候是采用从左到右深度优先原则匹配方法的.而新式类是采用C3算法
- 经典类没有MRO和instance.mro()调用的
假定存在以下继承关系:
class D(object):
def say_hello(self): pass class E(object): pass class B(D): pass class C(E): pass class A(B, C): pass
采用DFS(深度优先搜索算法)当调用了A的say_hello()方法的时候,系统会去B中查找如果B中也没有找到,那么去D中查找,很显然D中存在这个方法,但是DFS对于以下继承关系就会有缺陷:
class D(object):
pass class B(D): pass class C(D): def say_hello(self): pass class A(B, C): pass
在A的实例对象中调用say_hello方法时,系统会先去B中查找,由于B类中没有该方法的定义,所以会去D中查找,D类中也没有,系统就会认为该方法没有定义,其实该方法在C中定义了。所以考虑使用BFS(广度优先搜索算法),那么问题回到第一个继承关系,假定C和D具备重名方法,在调用A的实例的方法时,应该先在B中查找,理应调用D中的方法,但是使用BFS的时候,C类中的方法会覆盖D类中的方法。在Python 2.3以后的版本中,使用C3算法:
# 获取解析顺序的方法
类名.mro()
类名.__mro__
inspect.getmro(类名)
使用C3算法后的第二种继承顺序:
class D(object):
pass class B(D): pass class C(D): def say_hello(self): pass class A(B, C): pass print(A.mro()) # [, , , , ]
使用C3算法后的第一种继承顺序:
class D(object):
pass class E(object): pass class B(D): pass class C(E): pass class A(B, C): pass print(A.mro()) # [, , , , , ]
在这里仅介绍了算法的作用和演变历史,关于深入详细解析,请看我的其他文章——《从Python继承谈起,到C3算法落笔》。
No.10 类方法、实例方法和静态方法
class Demo(object):
# 类方法 @classmethod def class_method(cls, number): pass # 静态方法 @staticmethod def static_method(number): pass # 对象方法/实例方法 def object_method(self, number): pass
实例方法只能通过类的实例来调用;静态方法是一个独立的、无状态的函数,紧紧依托于所在类的命名空间上;类方法在为了获取类中维护的数据,比如:
class Home(object):
# 房间中人数 __number = 0 @classmethod def add_person_number(cls): cls.__number += 1 @classmethod def get_person_number(cls): return cls.__number def __new__(self): Home.add_person_number() # 重写__new__方法,调用object的__new__ return super().__new__(self) class Person(Home): def __init__(self): # 房间人员姓名 self.name = 'name' # 创建人员对象时调用Home的__new__()方法 tom = Person() print(type(tom)) # alice = Person() bob = Person() test = Person() print(Home.get_person_number())
No.11 数据封装和私有属性
Python中使用双下划线+属性名称实现类似于静态语言中的private修饰来实现数据封装。
class User(object):
def __init__(self, number): self.__number = number self.__number_2 = 0 def set_number(self, number): self.__number = number def get_number(self): return self.__number def set_number_2(self, number2): self.__number_2 = number2 # self.__number2 = number2 def get_number_2(self): return self.__number_2 # return self.__number2 u = User(25) print(u.get_number()) # 25 # 真的类似于Java的反射机制吗? print(u._User__number) # 25 # 下面又是啥情况。。。想不明白了T_T u.set_number_2(18) print(u.get_number_2()) # 18 print(u._User__number_2) # Anaconda 3.6.3 第一次是:u._User__number_2 第二次是:18 # Anaconda 3.6.5 结果都是 0 # 代码我改成了正确答案,感谢我大哥给我指正错误,我保留了错误痕迹 # 变量名称写错了,算是个写博客突发事故,这问题我找了一天,万分感谢我大哥,我太傻B了,犯了低级错误 # 留给和我一样的童鞋参考我的错我之处吧! # 正确结果: # 25 25 18 18
No.12 Python的自省机制
自省(introspection)是一种自我检查行为。在计算机编程中,自省是指这种能力:检查某些事物以确定它是什么、它知道什么以及它能做什么。自省向程序员提供了极大的灵活性和控制力。
- dir([obj]):返回传递给它的任何对象的属性名称经过排序的列表(会有一些特殊的属性不包含在内)
- getattr(obj, attr):返回任意对象的任何属性 ,调用这个方法将返回obj中名为attr值的属性的值
- ... ...
No.13 super函数
Python3.x 和 Python2.x 的一个区别是: Python 3 可以使用直接使用 super().xxx 代替 super(type[, object-or-type]).xxx 。
super()函数用来调用MRO(类方法解析顺序表)的下一个类的方法。
No.14 Mixin继承
在设计上将Mixin类作为功能混入继承自Mixin的类。使用Mixin类实现多重继承应该注意:
- Mixin类必须表示某种功能
- 职责单一,如果要有多个功能,就要设计多个Mixin类
- 不依赖子类实现,Mixin类的存在仅仅是增加了子类的功能特性
- 即使子类没有继承这个Mixin类也可以工作
class Cat(object):
def eat(self): print("I can eat.") def drink(self): print("I can drink.") class CatFlyMixin(object): def fly(self): print("I can fly.") class CatJumpMixin(object): def jump(self): print("I can jump.") class TomCat(Cat, CatFlyMixin): pass class PersianCat(Cat, CatFlyMixin, CatJumpMixin): pass if __name__ == '__main__': # 汤姆猫没有跳跃功能 tom = TomCat() tom.fly() tom.eat() tom.drink() # 波斯猫混入了跳跃功能 persian = PersianCat() persian.drink() persian.eat() persian.fly() persian.jump()
No.25 上下文管理器with语句与contextlib简化
普通的异常捕获机制:
try:
pass
except Exception as err:
pass else: pass finally: pass
with简化了异常捕获写法:
class Demo(object):
def __enter__(self): print("enter...") return self def __exit__(self, exc_type, exc_val, exc_tb): print("exit...") def echo_hello(self): print("Hello, Hello...") with Demo() as d: d.echo_hello() # enter... # Hello, Hello... # exit...
import contextlib
# 使用装饰器
@contextlib.contextmanager
def file_open(file_name): # 此处写__enter___函数中定义的代码 print("enter function code...") yield {} # 此处写__exit__函数中定义的代码 print("exit function code...") with file_open("json.json") as f: pass # enter function code... # exit function code...
No.26 序列类型的分类
- 容器序列:list tuple deque
- 扁平序列:str bytes bytearray array.array
- 可变序列:list deque bytearray array
- 不可变序列:str tuple bytes
No.27 +、+=、extend()之间的区别于应用场景
首先看测试用例:
# 创建一个序列类型的对象
my_list = [1, 2, 3]
# 将现有的序列合并到my_list extend_my_list = my_list + [4, 5] print(extend_my_list) # [1, 2, 3, 4, 5] # 将一个元组合并到这个序列 extend_my_list = my_list + (6, 7) # 抛出异常 TypeError: can only concatenate list (not "tuple") to list print(extend_my_list) # 使用另一种方式合并 extend_my_list += (6, 7) print(extend_my_list) # [1, 2, 3, 4, 5, 6, 7] # 使用extend()函数进行合并 extend_my_list.extend((7, 8)) print(extend_my_list) # [1, 2, 3, 4, 5, 6, 7, 7, 8]
由源代码片段可知:
class MutableSequence(Sequence):
__slots__ = ()
"""All the operations on a read-write sequence. Concrete subclasses must provide __new__ or __init__, __getitem__, __setitem__, __delitem__, __len__, and insert(). """ # extend()方法内部使用for循环来append()元素,它接收一个可迭代序列 def extend(self, values): 'S.extend(iterable) -- extend sequence by appending elements from the iterable' for v in values: self.append(v) # 调用 += 运算的时候就是调用该函数,这个函数内部调用extend()方法 def __iadd__(self, values): self.extend(values) return self
No.28 使用bisect维护一个已排序的序列
import bisect
my_list = []
bisect.insort(my_list, 2)
bisect.insort(my_list, 9)
bisect.insort(my_list, 5)
bisect.insort(my_list, 5) bisect.insort(my_list, 1) # insort()函数返回接收的元素应该插入到指定序列的索引位置 print(my_list) # [1, 2, 5, 5, 9]
No.29 deque类详解
deque是Python中一个双端队列,能在队列两端以$O(1)$的效率插入数据,位于collections模块中。
from collections import deque
# 定义一个双端队列,长度为3
d = deque(maxlen=3)
deque类的源码:
class deque(object):
""" deque([iterable[, maxlen]]) --> deque object 一个类似列表的序列,用于对其端点附近的数据访问进行优化。 """ def append(self, *args, **kwargs): """ 在队列右端添加数据 """ pass def appendleft(self, *args, **kwargs): """ 在队列左端添加数据 """ pass def clear(self, *args, **kwargs): """ 清空所有元素 """ pass def copy(self, *args, **kwargs): """ 浅拷贝一个双端队列 """ pass def count(self, value): """ 统计指定value值的出现次数 """ return 0 def extend(self, *args, **kwargs): """ 使用迭代的方式扩展deque的右端 """ pass def extendleft(self, *args, **kwargs): """ 使用迭代的方式扩展deque的左端 """ pass def index(self, value, start=None, stop=None): __doc__ """ 返回第一个符合条件的索引的值 """ return 0 def insert(self, index, p_object): """ 在指定索引之前插入 """ pass def pop(self, *args, **kwargs): # real signature unknown """ 删除并返回右端的一个元素 """ pass def popleft(self, *args, **kwargs): # real signature unknown """ 删除并返回左端的一个元素 """ pass def remove(self, value): # real signature unknown; restored from __doc__ """ 删除第一个与value相同的值 """ pass def reverse(self): # real signature unknown; restored from __doc__ """ 翻转队列 """ pass def rotate(self, *args, **kwargs): # real signature unknown """ 向右旋转deque N步, 如果N是个负数,那么向左旋转N的绝对值步 """ pass def __add__(self, *args, **kwargs): # real signature unknown """ Return self+value. """ pass def __bool__(self, *args, **kwargs): # real signature unknown """ self != 0 """ pass def __contains__(self, *args, **kwargs): # real signature unknown """ Return key in self. """ pass def __copy__(self, *args, **kwargs): # real signature unknown """ Return a shallow copy of a deque. """ pass def __delitem__(self, *args, **kwargs): # real signature unknown """ Delete self[key]. """ pass def __eq__(self, *args, **kwargs): # real signature unknown """ Return self==value. """ pass def __getattribute__(self, *args, **kwargs): # real signature unknown """ Return getattr(self, name). """ pass def __getitem__(self, *args, **kwargs): # real signature unknown """ Return self[key]. """ pass def __ge__(self, *args, **kwargs): # real signature unknown """ Return self>=value. """ pass def __gt__(self, *args, **kwargs): # real signature unknown """ Return self>value. """ pass def __iadd__(self, *args, **kwargs): # real signature unknown """ Implement self+=value. """ pass def __imul__(self, *args, **kwargs): # real signature unknown """ Implement self*=value. """ pass def __init__(self, iterable=(), maxlen=None): # known case of _collections.deque.__init__ """ deque([iterable[, maxlen]]) --> deque object A list-like sequence optimized for data accesses near its endpoints. # (copied from class doc) """ pass def __iter__(self, *args, **kwargs): # real signature unknown """ Implement iter(self). """ pass def __len__(self, *args, **kwargs): # real signature unknown """ Return len(self). """ pass def __le__(self, *args, **kwargs): # real signature unknown """ Return self<=value. """ pass def __lt__(self, *args, **kwargs): # real signature unknown """ Return selfpass def __mul__(self, *args, **kwargs): # real signature unknown """ Return self*value.n """ pass @staticmethod # known case of __new__ def __new__(*args, **kwargs): # real signature unknown """ Create and return a new object. See help(type) for accurate signature. """ pass def __ne__(self, *args, **kwargs): # real signature unknown """ Return self!=value. """ pass def __reduce__(self, *args, **kwargs): # real signature unknown