内容目录
In [7]:
"""
鸭子类型:
在多个类中实现同一个方法,那么这些类可以看成同一个类型
"""
class Cat:
def say(self):
print('这是猫')
class Dog:
def say(self):
print('这是狗')
class Duck:
def say(self):
print('这是鸭子')
animal_list = [Cat, Dog, Duck]
for animal in animal_list:
animal().say() # 多个类实现了同一个方法,那么就可以归为一类
这是猫 这是狗 这是鸭子 ['双双', '夏洛'] ['双双', '夏洛', '小明', '小红']
4.2抽象基类(abc模块)¶
In [15]:
# 作用1:判断对象中是否实现了计算长度的方法
from collections.abc import Sized
class Student:
def __init__(self, student_list):
self.student_list = student_list
def __len__(self, item):
return self.student_list[item]
stu = Student(['小明','小红'])
print(hasattr(stu, '__len__'))
print(isinstance(stu, Sized))
True True
In [22]:
# 作用2:强制子类实现get_cache和set_cache方法
from abc import ABC, abstractmethod
class Cache(ABC):
@abstractmethod
def get_cache(self):
pass
@abstractmethod
def set_cache(self, value):
pass
class TulingOnline(Cache):
def __init__(self):
self.cache = ''
def get_cache(self):
return self.cache
def set_cache(self,value):
self.cache = value
return True
tuling = TulingOnline()
tuling.set_cache(67)
tuling.get_cache()
Out[22]:
67
4.3使用isintance而不是type¶
In [29]:
class A:
pass
class B(A):
pass
b = B()
# 判断传入的对象是否是指定的类型
# 判断的对象也参考类的继承关系
print(isinstance(b, B))
print(isinstance(b, A))
# == 是判断两个值是否相等
print(type(b) == B)
print(type(b) == A)
# is判断的是内存地址
print(type(b) is B)
print(type(b) is A)
"""
总结:在判断对象的类型时尽量优先使用isinstance
"""
True True True False True False
4.4类变量和对象变量¶
In [35]:
class A:
# 类属性
a = 1
def __init__(self, b, c):
# 实例属性
self.b = b
self.c = c
obj_a = A(2, 3)
# 实例对象可以访问类属性和实例属性
print(obj_a.a, obj_a.b, obj_a.c)
# 类对象也可以访问类属性
print(A.a)
# 类对象无法访问实例属性
# print(A.b, A.c)
# 对类属性的值进行修改
A.a = 11
print(A.a)
print(obj_a.a)
# 实例对象修改类属性
obj_a.a = 22
print(A.a) # 实例属性无法修改类属性
print(obj_a.a) # 当前代码是通过示例对象新建了一个实例属性
1 2 3 1 11 11 11 22
4.5类属性和实例属性以及查找顺序¶
In [2]:
class A:
name = "cls_a"
def __init__(self):
self.name = 'obj_a'
# 类中的属性查找,优先查找实例属性,如果找不到向上查找类属性
a = A()
print(a.name)
print(A.name)
obj_a cls_a
In [5]:
# 菱形继承
class D:
name = "cls_d"
class B(D):
name = "cls_b"
# pass
class C(D):
name = "cls_c"
class A(B, C):
pass
a = A()
print(a.name)
print(A.__mro__) # 类A的继承关系链:A→B→C→D→Object
cls_b (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class 'object'>)
In [7]:
# 树状继承
class E:
name = "cls_e"
class D:
name = "cls_d"
class B(D):
name = "cls_b"
# pass
class C(E):
name = "cls_c"
class A(B, C):
pass
a = A()
print(a.name)
print(A.__mro__) # 查找顺序:A→B→D→C→E→Object
cls_b (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.C'>, <class '__main__.E'>, <class 'object'>)
4.6静态方法、类方法以及对象方法¶
In [39]:
class Student:
address = '长沙'
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
def __str__(self):
return f'{self.name}, {self.age}, {self.gender}'
def set_info(self):
self.age += 1
print(self.address)
@staticmethod
def parse_from_string(stu_info):
name, age, gender = tuple(stu_info.split(','))
return Student(name, age, gender)
@classmethod
def parse_from_string_ex(cls, stu_info):
name, age, gender = tuple(stu_info.split(','))
return cls(name, age, gender)
stu_1 = Student('小明', 18, '男')
print(stu_1)
stu_1.age = 20
print(stu_1)
# 实例方法
stu_1.set_info()
print(stu_1)
# 静态方法
stu_2 = Student.parse_from_string('小红,18,女')
print(stu_2)
# 类方法
stu_3 = Student.parse_from_string_ex('大熊,40,男')
print(stu_3)
# 实例方法:
# 方法中的第一个参数是self,实例方法可以访问类中的所有属性和方法
#
# 静态方法@staticmethod:
# 场景:在文件中保存了一些学生的信息,需要将文件中的信息导入到类中进行处理。
# 格式:'学生姓名,年龄,性别'
#
# 类方法@classmethod:
# 可以被类对象和实例对象调用,类方法可以访问类属性
#
# 1、如果当前的需求不需要创建实例对象,并且不需要访问实例属性/类属性,可以使用静态方法
# 2、如果当前的需求不需要创建实例对象,但是需要访问类属性,可以优先使用类方法
# 3、如果当前的需求需要实例对象,并且需要访问类中的所有属性和方法,则使用实例方法
小明, 18, 男 小明, 20, 男 长沙 小明, 21, 男 小红, 18, 女 大熊, 40, 男
4.7数据封装和私有属性¶
In [22]:
class Person:
def __init__(self, name, money):
self.name = name
self.__money = money
def get_money(self):
print(self.__money)
p = Person('小明', 10000)
# print(p.name, p.__money) # 直接通过实例对象是无法访问的
p.get_money()
10000
4.8Python对象的自省机制¶
自省机制: 检测一个类的内部结构
- 1、Python中对象都具有一个特殊的属性:dict 只能查询属于自身的属性
- 2、Python中dir函数可以查询一个对象中所有的属性和方法
In [26]:
class Person:
name = 'user'
class Student(Person):
def __init__(self, school_name):
self.school_name = school_name
stu = Student('兰州大学')
# 查询这个对象中包含的属性
print(stu.__dict__)
print(Person.__dict__)
stu.__dict__['address'] = '长沙'
print(stu.__dict__)
# dir函数可以查询一个对象中的所有属性和方法,包含这个对象的父类
print(dir(stu))
{'school_name': '兰州大学'} {'__module__': '__main__', 'name': 'user', '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None} {'school_name': '兰州大学', 'address': '长沙'} ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'address', 'name', 'school_name']
4.9super函数¶
1、Super函数的作用:调用’父类’中的方法
2、Super函数使用的场景:在代码重用的场景下使用
3、Super函数的运行过程:super()不是简单的调用一个类的父类,而是按照mro顺序进行查询并调用
In [33]:
class A:
def __init__(self):
print('A')
class B(A):
def __init__(self):
print('B')
# 如果想在B的init函数中也调用父类A的init函数,有两种方法:
# 方法1:
# A.__init__(self)
# 方法2:
super().__init__()
b = B()
B A
In [34]:
# 需要创建一个类,让类具有多线程执行的特征
import threading
class MyThread(threading.Thread):
def __init__(self, thread_name, user):
self.user = user
# 不必重新维护一个thread_name,而是可以复用父类的Thread中init的name初始化
# self.thread_name = thread_name
super().__init__(name = thread_name)
In [35]:
class D:
def __init__(self):
print('d')
class C(D):
def __init__(self):
print('c')
super().__init__()
class B(D):
def __init__(self):
print('b')
super().__init__()
class A(B, C):
def __init__(self):
print('a')
super().__init__()
a = A()
# super()本质上是按照__mro__顺序调用方法
a b c d
4.10django rest framework中对多继承使用经验¶
In [38]:
class Animal:
def __init__(self, name):
self.name = name
class RunMixin:
def run(self):
print(f'{self.name} 正在跑...')
class SwimMixin:
def swim(self):
print(f'{self.name} 正在游泳...')
class FlyMixin:
def fly(self):
print(f'{self.name} 正在飞...')
class Duck(Animal, RunMixin, SwimMixin, FlyMixin):
pass
duck = Duck('鸭子')
duck.run()
duck.swim()
duck.fly()
# 当前的继承方式是一种混合继承
# 1、mixin功能是单一的
# 2、mixin类不继承其他的类(除了object)
# 使用注意事项:
# 1、mixin因为功能简单,并且没有复杂的继承关系,特别好管理
# 2、在使用mixin的时,应该尽量避免在子类中使用super
鸭子 正在跑... 鸭子 正在游泳... 鸭子 正在飞...
In [43]:
# try语句的基本使用
try:
print('程序运行....')
raise KeyError
except KeyError:
print('key error 错误...')
else:
print('程序未产生异常时则运行当前代码...')
finally:
print('程序无论是否出现异常都执行此语句...')
# 在函数中使用try语句
def func_try_except():
try:
print('程序运行....')
# raise KeyError
return 1
except KeyError:
print('key error 错误...')
return 2
else:
print('程序未产生异常时则运行当前代码...')
return 3
finally:
print('程序无论是否出现异常都执行此语句...')
# 如果finally中出现return,则优先返回finally中的返回值
return 4
res = func_try_except()
print(res)
程序运行.... key error 错误... 程序无论是否出现异常都执行此语句... 程序运行.... 程序无论是否出现异常都执行此语句... 4
2、使用With VS 使用try_except的对比¶
In [48]:
# 上下文管理协议
# 1、通过上下文管理协议实现try-except会相对简单,
# 2、使用上下文管理协议时,需要实现对应的魔术方法
class Sample:
def __enter__(self):
try:
self.file_obj = open('test.txt')
except FileNotFoundError:
self.file_obj = None
print('我被执行了:__enter__')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('我被执行了:__exit__')
if self.file_obj is None:
print('当前文件不存在')
else:
self.file_obj.close()
def run(self):
print('程序启动...')
# 以下使用With之后,相比try..except..else..finally的写法要简单很多
with Sample() as sample:
sample.run()
我被执行了:__enter__ 程序启动... 我被执行了:__exit__ 当前文件不存在
4.12contextlib实现上下文管理器¶
In [50]:
import contextlib
# 增加以下装饰器,则函数就支持上下文管理协议
@contextlib.contextmanager
def open_file(file_name):
print(f'open: {file_name}')
# __enter__必须在yield的上面
# 被contextmanger装饰的函数必须是一个生成器函数
yield {'name': '小明', 'age': 30}
# __exit__必须在yield的下面
print(f'close:{file_name}')
with open_file('测试.txt') as f_open:
print('程序启动...')
open: 测试.txt 程序启动... close:测试.txt
In [ ]: