Python高级编程
Type、Object与Class关系
type创建所有对象,包括它自己本身,这便是一切皆对象的缘由,而所有类的基类都是object
抽象基类与鸭子类型
from abc import ABCMeta,abstractmethod #(抽象方法) |
这是抽象基类的用法,特点是在构造一个
Payment
抽象基类后,它不能直接实例化,只能被其他类去继承,如Alipay和WeChatPay两个类,继承后的类必须要复现抽象基类里面的方法,否则报错!
此代码完美解释了什么是鸭子类型,Order
类中的方法account
要求传入一个对象,并且只要求其含有pay
方法,因而不管它是什么对象,只要有pay方法,就认为它与Payment
基类一致
类变量与实例变量
class A: |
这里aa
变量为A类的类变量,其创造的所有实例共享,可以通过A.aa = 20
来修改该类变量,但是不能通过实例对象来修改类变量,如:
test = A(3,4) |
test.aa=20
这个会给test实例添加一个新的属性aa
类和实例属性的查找顺序
class D: |
输出结果:
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class 'object'>)
表明这种菱形结构的继承关系查找顺序是:A–>B–>C–>D–>object
class D: |
输出结果是:
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.C'>, <class '__main__.E'>, <class 'object'>)
表明继承顺序是:A–>B–>D–>C–>E–>object
实例方法、类方法、静态方法
参考链接
class Date: |
输出结果:日期为:2019年9月11日 日期为:2022年12月36日 日期为:2023年12月36日
静态方法、类方法使用区别或者说使用场景:
- 模拟构造函数_init_()多次初始化类
- 类方法是将类本身作为对象进行操作的方法。假设有个方法,且这个方法在逻辑上采用类本身作为对象来调用更合理,那么这个方法就可以定义为类方法。另外,如果需要继承,也可以定义为类方法
- 静态方法是类中的函数,不需要实例。静态方法主要是用来存放逻辑性的代码,逻辑上属于类,但是和类本身没有关系,也就是说在静态方法中,不会涉及到类中的属性和方法的操作。可以理解为,静态方法是个独立的、单纯的函数,它仅仅托管于某个类的名称空间中,便于使用和维护。
super()函数
- 为什么要在子类中调用父类的构造函数?
- super的执行顺序
一、有些子类需要利用父类init中的一些属性,列如多线程中使用Thread模块中的name属性,就不用自己再写一次了
from threading import Thread |
super
的作用:
-
如果子类(Puple)继承父类(Person)不做初始化(_init_),那么会自动继承父类(Person)属性name。
注:此时子类实例化时必须传入参数name,否则报错!
-
如果子类(Puple_Init)继承父类(Person)做了初始化,且不调用super初始化父类构造函数,那么子类(Puple_Init)不会自动继承父类的属性(name)。
-
如果子类(Puple_super)继承父类(Person)做了初始化,且调用了super初始化了父类的构造函数,那么子类(Puple_Super)也会继承父类的(name)属性。
-
在子类中一个和父类重名的方法中调用父类的方法
super(Child,self).fun()
,等效于直接写父类名.fun(self)
,但是这种硬编码的方式不太好。super(A,self).__init__()
为子类调用父类初始化的写法。
二、super()._init_()在多继承中是根据mro方式来执行的,如:
class A: |
执行顺序:D–>B–>A–>C
传递参数(list,dict)时引发的问题!
引入:
def add(a,b): |
结果是a为[1,2,3,4]
,c也为[1,2,3,4]
,但是如果传递的a是元组(1,2)
,则a的值不会改变,其原因是对于list这种类型的结构,它可以被修改
进一步:
class Company: |
输出结果:['bobby1', 'bobby3']
但是,如果是这样:
com2 = Company("com2") |
打印结果:
其原因在于Company
对象默认的值为一个空列表,因而如果创建对象时未传递列表将会默认使用这个空列表,也就是后续对象都将指向这个默认值容器,因此容易出错,改用元组将会避免此bug。
__new__()与_init_()
参考资料
元类编程
引入:若想动态地创建类,则常用的方式是在函数中创建,如下:
def create_class(name): |
由前述可知,type能够创建一切对象,故可考虑用type来动态的创建类,type的源代码为:
#要给type创建类添加的方法 |
其中cls = type('human', (), {'name': 'human', 'say_name': say_name})
,第二个参数代表这个类要继承的基类,如写了某个类叫Baseclass
,则该参数为(Baseclass, )
逗号必须加,否则抛出异常!,若没有需要继承的类必须加()
,第三个参数是类的属性和方法
这里便能更好的理解元类,所谓元类就是类的类。type本身就是类,由它还可以创建类并能实例化对象。这里就能理解前面所说的对象是由类创建的,而类本身又是对象,它是由type创建的。
python中*args和**kwargs的理解
- *args和**kwargs主要用于定义函数的可变参数*
- *args:发送一个非键值对的可变数量的参数列表给函数
- **kwargs:发送一个键值对的可变数量的参数列表给函数
- 如果想要在函数内使用带有名称的变量(像字典那样),那么使用**kwargs。
def foo(a, b, *number, c): |
注:和序列解包不同,这里c并不会分配参数,而是剩下的参数都被number获取
def bar(**number): |
结果是:{'a': 1, 'b': 2, 'c': 3}
参考博客