赞
踩
众所周知,Java中强调“一切皆对象”,但是Python中的面向对象比Java更加彻底,因为Python中的类(class)也是对象,函数(function)也是对象,而且Python的代码和模块也都是对象。
- # 首先创建一个函数和一个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
- # 将函数和类添加到集合中
- obj_list = []
- obj_list.append(Demo)
- obj_list.append(function)
- # 遍历列表
- for i in obj_list:
- print(i)
- # <class '__main__.Demo'>
- # <function function at 0x0000020D681B3E18>
- # 定义一个具体函数
- def test_func(class_name, func_name):
- class_name()
- func_name()
- # 将类名和函数名传入形参列表
- test_func(Demo, function)
- # Demo Class
- # function
- # 定义函数实现返回类和函数
- def test_func2():
- return Demo
-
- def test_func3():
- return function
- # 执行函数
- test_func2()() # Demo Class
- test_func3()() # function
在Python中,object
的实例是type
,object
是顶层类,没有基类;type
的实例是type
,type
的基类是object
。Python中的内置类型的基类是object
,但是他们都是由type
实例化而来,具体的值由内置类型实例化而来。在Python2.x的语法中用户自定义的类没有明确指定基类就默认是没有基类,在Python3.x的语法中,指定基类为object
。
- # object是谁实例化的?
- print(type(object)) # <class 'type'>
-
- # object继承自哪个类?
- print(object.__bases__) # ()
-
- # type是谁实例化的?
- print(type(type)) # <class 'type'>
-
- # type继承自哪个类?
- print(type.__bases__) # (<class 'object'>,)
-
- # 定义一个变量
- value = 100
-
- # 100由谁实例化?
- print(type(value)) # <class 'int'>
-
- # int由谁实例化?
- print(type(int)) # <class 'type'>
-
- # int继承自哪个类?
- print(int.__bases__) # (<class 'object'>,)
- # Python 2.x的旧式类
- class OldClass():
- pass
-
- # Python 3.x的新式类
- class NewClass(object):
- pass
在Python中,对象有3个特征属性:
id()
函数进行查看在Python解释器启动时,会创建一个None类型的None对象,并且None对象全局只有一个。
在Python中,迭代类型可以使用循环来进行遍历。
Python中的魔法函数使用双下划线开始,以双下划线结尾。关于详细介绍请看我的文章——《全面总结Python中的魔法函数》。
鸭子类型是程序设计中的推断风格,在鸭子类型中关注对象如何使用而不是类型本身。鸭子类型像多态一样工作但是没有继承。鸭子类型的概念来自于:“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”
- # 定义狗类
- 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)
。
我们可以使用猴子补丁
来实现协议,那么什么是猴子补丁呢?
猴子补丁就是在运行时修改模块或类,不去修改源代码,从而实现目标协议接口操作,这就是所谓的打猴子补丁。
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)
。
- 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
- # 此处的类也是模板对象,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
object
,即使不显式继承也会默认继承自object
假定存在以下继承关系:
- 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()) # [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class 'object'>]
使用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())
- # [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.C'>, <class '__main__.E'>, <class 'object'>]
在这里仅介绍了算法的作用和演变历史,关于深入详细解析,请看我的其他文章——《从Python继承谈起,到C3算法落笔》。
- 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)) # <class '__main__.Person'>
- alice = Person()
- bob = Person()
- test = Person()
-
- print(Home.get_person_number())
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
自省(introspection)是一种自我检查行为。在计算机编程中,自省是指这种能力:检查某些事物以确定它是什么、它知道什么以及它能做什么。自省向程序员提供了极大的灵活性和控制力。
Python3.x 和 Python2.x 的一个区别是: Python 3 可以使用直接使用 super().xxx 代替 super(type[, object-or-type]).xxx 。
super()函数用来调用MRO(类方法解析顺序表)的下一个类的方法。
在设计上将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()
普通的异常捕获机制:
- 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...
首先看测试用例:
- # 创建一个序列类型的对象
- 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):
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。