Python高级知识点学习(三)
mro算法
类属性和实例属性的查找顺序
- 何为类属性:定义在类内部的的一些变量或者方法,都统称为类属性
- 何为实例属性:定义在对象内部的的一些变量或者方法,都统称为实例属性
对象也就是实例的意思。
class A:
aa = 1
def __init__(self, x, y):
self.x = x
self.y = y
a = A(2, 3)
类也是对象,看上边代码,实际上有两个空间,A 和 a 两个不同的空间。
单继承时,属性查找方式,向上查找,首先查找对象里,再查找类中
在多继承时,会很复杂
python2.2之前,python里的类叫经典类,经典类继承方式如果不显式继承object,实际上是不会自动继承object,Python3中,经典类已经不存在了,都叫做新式类。
经典类中,深度优先查找 。
Python2.3之后,广度优先也没有了,至今都采用C3算法
Python3多重继承C3算法:
#新式类
class D:
pass
class E:
pass
class C(E):
pass
class B(D):
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'>)
可以看到继承顺序是 A - B - D - C - E - object
类方法静态方法和实例方法
实例方法:实例方法很常见,通常我们在类里定义的都是实例方法,
- 只是针对于实例进行操作,实例方法中第一个参数:self,self代表的就是实例本身
静待方法:
- 带@staticmethod装饰器的方法叫静态方法,静态方法不需要接受cls或self,和普通的函数用法一样
类方法:
- 带@classmethod装饰器的方法叫做类方法,类方法第一个参数是cls,代表的是类本身,(这里的cls可以修改为任意形式的代表)
def a(self):
pass
@staticmethod
def b():
pass
@classmethod
def c(cls):
pass
Python中的私有属性
双下划綫开头表示私有属性,私有属性的访问,只能在类中的公共方法中访问,类外部防问不到,无法通过实例访问。
私有属性不仅仅是变量 还可以是函数。
class User:
def __init__(self, birthday):
self.__birthday = birthday
user = User(2000)
print(user.birthday)
运行结果:
AttributeError: 'User' object has no attribute 'birthday'
以上代码块结果就是访问不到私有属性。
但是,私有不是绝对的,只是加了一个小技巧,Python中将私有属性的访问变形成这种:
class User:
def __init__(self, birthday):
self.__birthday = birthday
user = User(2000)
print(user._User__birthday)
运行结果:
2000
可以看到,通过变量_User__birthday
这个就可以访问到私有属性。
Java中的反射机制也是无法做到绝对安全的,从语言层面讲,没有绝对的私有属性,Python简单一些 Java麻烦一些。
Python自省
Python对象自省
何为自省?
自省是通过一定的机制查询到对象的内部结构
使用__dict__
魔法函数,dict是用C语言写的 性能高,做了很多优化,推荐使用。
也可以使用dir() ,dir()会列出类中所有属性,推荐使用。
class Student():
def __init__(self, scool_name):
self.scool_name = scool_name
if __name__ == "__main__":
user = Student("zhao")
#通过__dict__查询属性
print(user.__dict__)
打印结果:
{'scool_name': 'zhao'}
class Student():
def __init__(self, name):
self.name = name
if __name__ == "__main__":
user = Student("zhao")
user.__dict__["school_addr"] = "北京市"
print(user.school_addr)
print(user.name)
a = [1, 2]
print(dir(a))
print(dir(user))
打印结果:
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name', 'school_addr', 'scool_name']
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
Python中super()函数
super()就是调用父类。
class A:
def __init__(self):
print('is A')
class B(A):
def __init__(self):
print('is B')
# 在某些情况下,我们希望在运行完以上代码调用父类的init方法,一般在Python3中使用下面这种方法调用
super().__init__()
if __name__ == "__main__":
b = B()
打印结果:
is B
is A
super()就是调用父类,其实这样讲并不准确,super()函数调用其实是按照mro查找的顺序调用的。
class A:
def __init__(self):
print("A")
class B(A):
def __init__(self):
print("B")
super().__init__()
class C(A):
def __init__(self):
print("C")
super().__init__()
class D(B, C):
def __init__(self):
print("D")
super().__init__()
if __name__ == "__main__":
print(D.__mro__)
d = D()
打印结果:
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
D
B
C
A
既然我们重写子类的构造函数, 为什么还要去调用super再次调用父类构造函数?
有些时候,为了使用父类中的一些已经写好的方法,所以会有使用super的情况。
上下文管理器
首先介绍下这种用法:
try:
print("code started")
raise KeyError
return 1
except KeyError as e:
print ("key error")
return 2
else:
print("other error")
return 3
finally:
print ("finally")
return 4
result = exe_try()
print (result)
运行结果:
code started
key error
finally
4
在finally语句块中的代码,不管上边代码是否发生异常都会执行finally中的代码,优先finally中的return,其次return上边的。
上下文管理器,也就是with语句,实质上就是为了解放try finally这种写法而诞生的。
上下文管理器是如何完成的呢?
python是基于协议进行编程的,上下文管理器就是一种协议:上下文管理器协议。
上下文管理器协议可以使用是因为实现了两个魔法函数:
__enter__
和__exit__
class Sample:
def __enter__(self):
print("enter")
# 获取资源
return self
def __exit__(self, exc_type, exc_val, exc_tb):
# 释放资源
print("exit")
def do_something(self):
print("doing something")
with Sample() as sample:
sample.do_something()
运行结果:
enter
doing something
exit
还有一种方法,可以简便的实现上下文管理器的功能:
@contextlib.contextmanager
可以将一个函数变为上下文管理器
import contextlib
@contextlib.contextmanager
def file_open(file_name):
print ("file open")
yield {}
print ("file end")
with file_open("a.txt") as f_opened:
print("file processing")
运行结果:
file open
file processing
file end
@contextlib.contextmanager
内部会做一些逻辑,其实是利用了生成器的特性。