文章目录
1.Python中的多态
先来看一段代码
class Cat(object):
def say(self):
print("I am a cat")
class Dog(object):
def say(self):
print("I am a dog")
class Duck(object):
def say(self):
print("I am a duck")
要想调用say方法,一般写法是:
animal1 = Cat() #实例化一个类
animal1.say() #调用say方法
animal2 = Dog()
animal2.say()
animal3 = Duck()
animal3.say()
输出结果
I am a cat
I am a dog
I am a duck
接下来我们改写这段代码,把类放进一个列表里,然后去遍历它。
class Cat(object):
def say(self):
print("I am a cat")
class Dog(object):
def say(self):
print("I am a dog")
class Duck(object):
def say(self):
print("I am a duck")
animal_list = [Cat, Dog, Duck]
for animal in animal_list:
animal().say()
输出结果
I am a cat
I am a dog
I am a duck
这里animal可以指向列表中的任何一个类,而这些类中都实现了say方法,这些类就可以看做是同一类的类,say方法中有不同的实现,这就实现了多态。
使用多态后,方法接收的参数类型可以不同,只要满足父类型即可。
a = ["bobby1", "bobby2"]
b = ["bobby2", "bobby"]
name_tuple = ("bobby3", "bobby4")
name_set = set()
name_set.add("bobby5")
name_set.add("bobby6")
a.extend(b)
print(a)
a = ["bobby1", "bobby2"]
a.extend(name_tuple)
print(a)
a = ["bobby1", "bobby2"]
a.extend(name_set)
print(a)
输出结果
['bobby1', 'bobby2', 'bobby2', 'bobby']
['bobby1', 'bobby2', 'bobby3', 'bobby4']
['bobby1', 'bobby2', 'bobby6', 'bobby5']
可以看到无论是列表、元组还是集合类型,都可以传入extend方法,只要它是一个可迭代对象即可。
再来看到一段代码
a = ["bobby1", "bobby2"]
class Company(object):
def __init__(self, employee_list):
self.employee = employee_list
def __getitem__(self, item):
return self.employee[item]
company = Company(["tom", "bob", "jane"])
a.extend(company)
print(a)
输出结果
['bobby1', 'bobby2', 'tom', 'bob', 'jane']
company也是一个可迭代对象,所以可以传入extend方法。
2.抽象基类
什么是抽象基类?
- 在这个类中设定了一些方法,所有继承这个类的子类都必须实现这个方法。
- 抽象基类不可以实例化。
为什么要用抽象基类?抽象基类的两种应用场景:
(1)检查某个类是否有某种方法
class Company(object):
def __init__(self, employee_list):
self.employee = employee_list
def __len__(self):
return len(self.employee)
com = Company(["bobby1", "bobby2"])
print(hasattr(com, "__len__"))
print(len(com))
输出结果
True
2
这个对象包含__ len __方法,可以对这个对象使用len函数。
(2)判定某个对象的类型
class A:
pass
class B:
pass
b = B()
print(isinstance(b, A))
输出结果
False
b不是A类型。
有时候我们需要强制某个子类必须实现某些方法,比如说我们实现了一个web框架,继承cache(redis, cache, memorycache),需要设计一个抽象基类, 指定子类必须实现这些方法,如果子类不实现,就会出错。
模拟一个抽象基类
(1)
class CacheBase():
def get(self, key):
raise NotImplementedError
def set(self, key, value):
raise NotImplementedError
class RedisCache(CacheBase):
def set(self, key, value):
pass
redis_cache = RedisCache()
redis_cache.set("key", "value")
redis_cache.get("key")
输出结果
raise NotImplementedError
NotImplementedError
没有实现get方法,抛出了异常。这种方法在使用的时候才会抛出异常。
(2)
import abc
from collections.abc import *
class CacheBase(metaclass=abc.ABCMeta):
@abc.abstractmethod
def get(self, key):
pass
@abc.abstractmethod
def set(self, key, value):
pass
class RedisCache(CacheBase):
def set(self, key, value):
pass
redis_cache = RedisCache()
输出结果
redis_cache = RedisCache()
TypeError: Can't instantiate abstract class RedisCache with abstract methods get
这种方法在初始化的时候,如果没有实现方法,就会抛出异常。
3.isinstance和type
class A:
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
isinstance判断对象是不是某种的类型,会根据继承链去找继承关系,type只是判断对象的类型,A和B在内存中是两个不同的对象,因此用type判断得到的只是它们本身的类型。我们推荐使用isinstance来判断对象的类型而不是type。
4.类变量和实例变量
(1)
class A:
aa = 1 #类变量
def __init__(self, x, y):
self.x = x #实例变量
self.y = y #实例变量
a = A(2, 3)
print(a.x, a.y, a.aa)
print(A.aa)
输出结果
2 3 1
1
可以看到a.aa输出了1,说明实例变量在实例中找不到aa变量时,就会向上查找类变量。
(2)
class A:
aa = 1
def __init__(self, x, y):
self.x = x
self.y = y
a = A(2, 3)
A.aa = 11
print(a.x, a.y, a.aa)
print(A.aa)
输出结果
2 3 11
11
对类变量进行修改,实例对象在查找时也会查找到修改后的类变量。
(3)
class A:
aa = 1
def __init__(self, x, y):
self.x = x
self.y = y
a = A(2, 3)
A.aa = 11 #修改类变量
a.aa = 100 #赋值给实例属性,新建一个aa属性,放到实例a当中
print(a.x, a.y, a.aa)
print(A.aa)
输出结果
2 3 100
11
使用A.aa时,使用的是类变量,使用a.aa时,会在实例中新建一个aa变量。
5.类变量和实例变量的查找顺序
(1)
class A:
name = "A"
def __init__(self):
self.name = "obj"
a = A()
print(a.name)
输出结果
obj
class A:
name = "A"
# def __init__(self):
# self.name = "obj"
a = A()
print(a.name)
输出结果
A
可以看到查找顺序是由下往上查找。
(2)
图1的继承顺序
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
图2的继承顺序
class D:
pass
class C(D):
pass
class B(D):
pass
class A(B, C):
pass
print(A.__mro__)
输出结果
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class 'object'>)
在查找变量时的查找顺序是A > B > C > D > E
6.实例方法、静态方法和类方法
基础代码
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
def __str__(self):
return "{year}/{month}/{day}".format(year=self.year, month=self.month, day=self.day)
if __name__ == "__main__":
new_day = Date(2020, 5, 1)
print(new_day)
输出结果
2020/5/1
(1)实例方法
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
def tomorrow(self): #这是一个实例方法,作用是将实例的属性day的值+1
self.day += 1
def __str__(self):
return "{year}/{month}/{day}".format(year=self.year, month=self.month, day=self.day)
if __name__ == "__main__":
new_day = Date(2020, 5, 1)
new_day.tomorrow()
print(new_day)
输出结果
2020/5/2
(2)静态方法
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
def tomorrow(self):
self.day += 1
@staticmethod
def parse_from_string(date_str): #静态方法,作用是拆分日期字符串
year, month, day = tuple(date_str.split("-"))
return Date(int(year), int(month), int(day))
def __str__(self):
return "{year}/{month}/{day}".format(year=self.year, month=self.month, day=self.day)
if __name__ == "__main__":
date_str = "2020-5-1"
new_day = Date.parse_from_string(date_str) #调用时需要用Date调用,因为方法在属于类
print(new_day)
输出变量
2020/5/1
(3)类方法
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
def tomorrow(self):
self.day += 1
@classmethod
def parse_from_string(cls, date_str): #cls接收的是类本身
year, month, day = tuple(date_str.split("-"))
return cls(int(year), int(month), int(day)) #在更改类名的时候,这里可以不用变
def __str__(self):
return "{year}/{month}/{day}".format(year=self.year, month=self.month, day=self.day)
if __name__ == "__main__":
date_str = "2020-5-1"
new_day = Date.parse_from_string(date_str)
print(new_day)
输出结果
2020/5/1
(4)静态方法应用场景之一:只需要做判断的时候
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
def tomorrow(self):
self.day += 1
@staticmethod
def valid_string(date_str):
year, month, day = tuple(date_str.split("-"))
if int(year)>0 and (int(month)>0 and int(month)<=12) and (int(day)>0 and int(day)<=31):
return True
else:
return False
def __str__(self):
return "{year}/{month}/{day}".format(year=self.year, month=self.month, day=self.day)
if __name__ == "__main__":
date_str = "2020-5-32"
new_day = Date.valid_string(date_str)
print(new_day)
输出结果
False
7.数据封装和私有属性
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
def __str__(self):
return "{year}/{month}/{day}".format(year=self.year, month=self.month, day=self.day)
class User:
def __init__(self, birthday):
self.__birthday = birthday #私有属性
def get_age(self):
return 2020 - self.__birthday.year
if __name__ == "__main__":
user = User(Date(1999, 4, 8))
print(user._User__birthday) #python只是将属性名做了变换,并没有绝对私有化
print(user.get_age()) #只能通过类中的方法访问
输出结果
1999/4/8
21
8.Python对象的自省机制
自省是通过一定的机制查询到对象的内部结构。
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
def __str__(self):
return "{year}/{month}/{day}".format(year=self.year, month=self.month, day=self.day)
class Person:
"""
People
"""
name = "user"
class Student(Person):
def __init__(self, school_name):
self.school_name = school_name
if __name__ == "__main__":
user = Student("HNU")
#通过__dict__查询属性
print(user.__dict__) #列出对象的属性和值
user.__dict__["school_addr"] = "长沙市" #添加一个属性
print(user.school_addr)
print(Person.__dict__)
print(user.name) #列出类的name属性
print(dir(user)) #列出对象的所有属性
输出结果
{'school_name': 'HNU'}
长沙市
{'__module__': '__main__', '__doc__': '\n People\n ', 'name': 'user', '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>}
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', 'school_name']
9.super函数
(1)
class A:
def __init__(self):
print("A")
class B(A):
def __init__(self):
print("B")
super().__init__()
if __name__ == "__main__":
b = B()
输出结果
B
A
先调用B中的__ init ,再调用A中的 init __。
(2)既然我们重写B的构造函数,为什么还要去调用super()?
一个应用场景是在多线程中,实现代码的复用。
from threading import Thread
class MyThread(Thread):
def __init__(self, name, user):
self.user = user
super().__init__(name=name) #直接调用父类的方法,实现代码复用
(3)super()的执行顺序是什么?
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
可以看到和查找继承的顺序是一致的。
10.with语句
(1)try、except、finally
def exe_try():
try:
print ("code started")
raise KeyError
return 1
except KeyError:
print ("key error")
return 2
else:
print ("other error")
return 3
finally:
print ("finally")
print(exe_try())
输出结果
code started
key error
finally
2
代码先执行try中的语句,如果出现KeyError异常,就返回2,出现其他异常返回3,最后执行finally中的语句。这里我们抛出了一个KeyError异常,因此返回2。
我们把下面的语句注释的掉,再运行
#raise KeyError
运行结果
code started
finally
1
可以看到没有异常,返回1。我们把下面这句也注释掉。
#return 1
运行结果
code started
other error
finally
3
返回3,说明执行了else中的语句。
(2)上下文管理器协议
with语句用来管理上下文,可以自动关闭。
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
先进入获取资源,再执行do_something中的代码,最后释放资源。
11.contextlib上下文管理器
import contextlib
@contextlib.contextmanager
def file_open(file_name):
print("file open")
yield {}
print("file end")
with file_open("bobby.txt") as f_opened:
print("file processing")
运行结果
file open
file processing
file end