DAY2 PythonWeb全栈课程课堂内容
1. 抽象基类(承接Day1 PythonWeb全栈课程课堂内容)
- 抽象基类(abstract base class ABC)。抽象基类就是类里定义了纯虚成员函数的类。纯虚函数只提供了接口,并没有具体实现。抽象基类不能被实例化(不能创建对象),通常是作为基类供子类继承,子类中重写虚函数,实现具体的接口。
- 抽象基类就是定义各种方法而不做具体实现的类,任何继承自抽象基类的类必须实现这些方法,否则无法实例化。
应用场景:
- 检查某个类中是否还有某种方法。
- 强调某个子类必须实现某些方法。
特点:
- 抽象,不可以被实例化。
- 继承,必须得重写指定的方法。
# 创建抽象基类
import abc # 调用抽象基类模块abc
class A(metaclass=abc.ABCMeta): # 创建抽象基类A。
pass
class B(A): # 普通类继承抽象基类A
def deme(self):
pass
print(isinstance(B,A)) # False 原因A为抽象基类,B为实类,所以不属于同一类型,返回Falase
# -----------------------------
# 去掉B中的方法,测试B是否继承A。
import abc
class A(metaclass=abc.ABCMeta):
pass
class B(A):
pass
print(isinstance(B,A)) # False 原因A为抽象基类,B为实类,所以不属于同一类型,返回Falase。
# -----------------------------
import abc # 调用抽象基类模块abc
class A(metaclass=abc.ABCMeta): # 创建抽象基类A。
pass
class B(A): # 普通类继承抽象基类A
pass
# issubclass() 判断参数class 是否是类型参数classinfo 的子类
print(issubclass(B,A)) # True
# -----------------------------
# 使用register()方法,注册。 定义好的抽象基类,通过register()可以成为别的类的父类。(源文件使用较多)
import abc # 调用抽象基类模块abc
class A(metaclass=abc.ABCMeta): # 创建抽象基类A。
pass
class B(object): # 普通类继承抽象基类A
pass
A.register(B) # 将不是继承A类的B,强制变成A类的子类。
# issubclass() 判断参数class 是否是类型参数classinfo 的子类
print(issubclass(B,A)) # True
用途
'''*检查某个类中是否还有某种方法,应用场景。'''
class Demo():
def __init__(self, li):
self.li = li
def d(self):
pass
def __len__(self):
return len(self.li)
d = Demo(['Sam', 'Janice'])
print(len(d)) # 2
print(hasattr(d, '__len__')) # True 判断d 内是否有__len__这个魔术函数。
print(hasattr(d,'d')) # True 判断d 内是否有 d 这个方法。
from collection.abc import Sized, Iterable # Sized 和Iterable 是在其源码中是抽象基类。
print(isinstance(b, Sized)) # True 说明b 属于Sized 这个类型。原因Sized是一个抽象基类。
print(isinstance(b, Iterable)) # False
'''
当类中添加
def __iter__(self):
pass
时
'''
print(isinstance(b, Iterable)) # True 用于判断b 中是否有迭代方法__iter__。
'''必须得重写指定的方法。'''
import abc
class CacheBase(metaclass=abc.ABCMeta):
@abc.abstractmethod # 装饰器
def get(self, key): # 纯虚函数
pass
@abc.abstractmethod
def set(self, key, value): # 纯虚函数
pass
class RedisCache(CacheBase):
pass
cb = CacheBase()
'''
cb = CacheBase()
TypeError: Can't instantiate abstract class CacheBase with abstract methods get, set
''' # 抽象基类不能实例化。
rc = RedisCache()
'''
rc = RedisCache()
TypeError: Can't instantiate abstract class RedisCache with abstract methods get, set
'''# 必须重写,子类内部的方法
# -------------------------
'''必须得重写指定的方法。'''
import abc
class CacheBase(metaclass=abc.ABCMeta):
@abc.abstractmethod
def get(self, key):
pass
@abc.abstractmethod
def set(self, key, value):
pass
class RedisCache(CacheBase):
def get(self, key):
pass
def set(self, key, value):
pass
rc = RedisCache()
类与对象深度问题及解决技巧
1. 派生内置不可变类型
'''课堂内容推导'''
'''
案例:自定义一种新类型的元组,对于传入的可迭代对象,我们只保留其中int类型且值大于0的元素
'''
# 首先验证传输内容
class IntTuple(tuple):
pass
int_tuple = IntTuple([2, -2, 'Sam', ['x', 'y'], 4])
print(int_tuple) # (2, -2, 'Sam', ['x', 'y'], 4)
# -------------------------------
class IntTuple(tuple):
def __init__(self, iterable):
f = (i for i in iterable if isinstance(i, int) and i > 0)
print(f)
# <generator object IntTuple.__init__.<locals>.<genexpr> at 0x0000000009F89F10> generator是一个推导式
super().__init__(self, iterable)
'''
super().__init__(self, iterable)
TypeError: object.__init__() takes no parameters
''' # 表示__init__里面没有参数。
print(self) # (2, -2, 'Sam', ['x', 'y'], 4)
int_tuple = IntTuple([2, -2, 'Sam', ['x', 'y'], 4])
print(int_tuple)
class A:
def __new__(cls, *args, **kwargs):
print("A.__new__", *args, **kwargs)
def __init__(self):
print("A.__init__")
a = A()
'''
A.__new__
'''
class A:
def __new__(cls, *args, **kwargs):
print("A.__new__", *args, **kwargs)
return object.__new__(cls) # return值给self
def __init__(self):
print("A.__init__")
a = A()
'''
A.__new__
A.__init__
'''
class A:
def __new__(cls, *args, **kwargs):
print("A.__new__", *args, **kwargs)
return object.__new__(cls) # return值给self
def __init__(self, *args):
print("A.__init__", *args)
a = A(1, 2)
'''
A.__new__ 1 2
A.__init__ 1 2
'''
a = A.__new__(class, 1, 2)
A.__init__(a)
'''
A.__new__ 1 2
A.__init__ 1 2
'''
- 所以原先得改成__new__方法
class IntTuple(tuple):
def __new__(cls, iterable):
f = (i for i in iterable if isinstance(i, int) and i > 0)
return super().__new__(cls, f)
int_tuple = IntTuple([2, -2, 'Sam', ['x', 'y'], 4])
print(int_tuple)
'''
(2, 4)
'''
- __new__初始化魔法字符,在__init__之前。
2. 创建大量实例节省内存
- __slots__可以关闭动态绑定属性。
- __slots__不影响子类实例,不会继承,除非子类里面自己定义了 。
'''
在游戏开发中,有一个玩家类Player,每有一个在线玩家,在服务器内则有一个player的实例,当在线的人数很多时,将产生大量实例(百万级)
'''
class Player1(object):
def __init__(self, uid, name, status=0, level=1):
self.uid = uid
self.name = name
self.status = status
self.level = level
class Player2(object):
__slots__ = ('uid', 'name', 'status', 'level')
def __init__(self, uid, name, status=0, level=1):
self.uid = uid
self.name = name
self.status = status
self.level = level
p1 = Player1('001', 'Sam')
p2 = Player2('002', 'Janice')
print(p1.__dict__)
# {'uid': '001', 'name': 'Sam', 'status': 0, 'level': 1}
# print(p2.__dict__)
'''
print(p2.__dict__)
NameError: name 'p2' is not defined
''' # 说明p2中没有__dict__
# p2.gender = 1
'''
p2.gender = 1
AttributeError: 'Player2' object has no attribute 'gender'
''' # 静止动态添加,
# 证明减少
print(dir(p1))
'''
['__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__', 'level', 'name', 'status', 'uid']
'''
print(dir(p2))
'''
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'level', 'name', 'status', 'uid']
'''
print(set(dir(p1))-set(dir(p2)))
'''
{'__weakref__', '__dict__'}
''' # p1 比 p2 多 __weakref__ 和 __dict__
import sys
print(sys.getsizeof(p1)) # 56
print(sys.getsizeof(p1.__dict__)) # 112
print(sys.getsizeof(p2)) # 72
3. 上下文管理协议
- with调用类
class Sam(object):
def demo(self):
return 'Hellp Python'
with Sam() as sam:
sam.demo()
'''
with Sam() as sam:
AttributeError: __enter__
''' # 属性错误 缺少__enter__
class Sam(object):
def __enter__(self):
# 获取资源
print('start')
def demo(self):
return 'Hellp Python'
def __exit__(self, exc_type, exc_val, exc_tb):
# 释放资源
self.exc_type = exc_type
self.exc_val = exc_val
self.exc_tb = exc_tb
with Sam() as sam:
sam.demo()
'''
sam.demo()
AttributeError: 'NoneType' object has no attribute 'demo'
'''
class Sam(object):
def __enter__(self):
print('start')
return self # 返回self
def demo(self):
return 'Hellp Python'
def __exit__(self, exc_type, exc_val, exc_tb):
self.exc_type = exc_type
self.exc_val = exc_val
self.exc_tb = exc_tb
print('end')
with Sam() as sam:
print(sam.demo())
'''
start
Hellp Python
end
'''
class Sam(object):
def __enter__(self):
print('start')
return self
def demo(self):
return 'Hellp Python'
def __exit__(self, exc_type, exc_val, exc_tb):
# self.exc_type = exc_type
# self.exc_val = exc_val
# self.exc_tb = exc_tb
print(exc_type) # <class 'AttributeError'> 异常类
print(exc_val) # 'Sam' object has no attribute 'demo1' 异常值
print(exc_tb) # <traceback object at 0x000000000A019708> 追踪信息
print('end')
with Sam() as sam:
print(sam.demo1())
- 简化 - contextlib
def file_open(filename):
pass
with file_open('demo.txt') as f:
f.read()
'''
with file_open('demo.txt') as f:
AttributeError: __enter__
'''
import contextlib
@contextlib.contextmanager
def file_open(filename):
# __enter__
print("file open")
file_handler = open(filename, 'r', encoding='utf-8')
yield file_handler
# __exit__
print("file end")
file_handler.close()
with file_open('demo.txt') as f:
print(f.read())
'''
file open
sam
janice
mike
jerry
file end
'''
4. 让类支持比较操作
魔法方法 | 说明 |
---|---|
__gt__ | 大于 |
__lt__ | 小于 |
__gte__ | 大于等于 |
__lte__ | 小于等于 |
-
使用方法
# 常规类是不能比较的
class A:
pass
class B:
pass
print(A > B)
'''
print(A > B)
TypeError: '>' not supported between instances of 'type' and 'type'
'''
# 实现类的比较
class Rect:
def __init__(self, w, h):
self.w = w
self.h = h
def area(self):
return self.w * self.h
rect1 = Rect(1, 2)
rect2 = Rect(3, 4)
print(rect1 > rect2)
'''
print(rect1 > rect2)
TypeError: '>' not supported between instances of 'Rect' and 'Rect'
'''
class Rect:
def __init__(self, w, h):
self.w = w
self.h = h
def area(self):
return self.w * self.h
def __gt__(self, other): # 其中self是rect1,other是rect2
print(other) # <__main__.Rect object at 0x000000000295A748>
return self.area() > other.area() # False 不大于
rect1 = Rect(1, 2)
rect2 = Rect(3, 4)
print(rect1 > rect2) # False 不大于
print(rect1 < rect2) # True 小于
'''
其中类中只包含了 __gt__ 魔法函数,但是依旧可以比较小于。其实就是rect1 < rect2交换位置
实则是print(rect2 > rect1)
'''
from functools import total_ordering
@total_ordering
class Rect:
def __init__(self, w, h):
self.w = w
self.h = h
def area(self):
return self.w * self.h
def __gt__(self, other):
return self.area() > other.area()
def __eq__(self, other):
return self.area() == other.area()
rect1 = Rect(1, 2)
rect2 = Rect(3, 4)
print(rect1 >= rect2)
# 实现两个不同类之间的比较
from functools import total_ordering
import math
@total_ordering
class Rect:
def __init__(self, w, h):
self.w = w
self.h = h
def area(self):
return self.w * self.h
def __gt__(self, other):
return self.area() > other.area()
def __eq__(self, other):
return self.area() == other.area()
@total_ordering
class Circle():
def __init__(self, r):
self.r = r
def area(self):
return self.r ** 2 * math.pi
def __gt__(self, other):
return self.area() > other.area()
def __eq__(self, other):
return self.area() == other.area()
rect1 = Rect(1, 2)
rect2 = Rect(3, 4)
c = Circle(8)
print(c > rect1) # True
print(c >= rect2)
- 改写完善文件,使用抽象基类
from functools import total_ordering
from abc import ABCMeta, abstractclassmethod
import math
@total_ordering
class shape(metaclass=ABCMeta):
@abstractclassmethod
def area(self):
pass
def __gt__(self, other):
return self.area() > other.area()
def __eq__(self, other):
return self.area() == other.area()
class Rect(shape):
def __init__(self, w, h):
self.w = w
self.h = h
def area(self):
return self.w * self.h
class Circle(shape):
def __init__(self, r):
self.r = r
def area(self):
return self.r ** 2 * math.pi
rect1 = Rect(1, 2)
rect2 = Rect(3, 4)
c = Circle(8)
print(c > rect1) # True
print(c >= rect1) # True
5. 通过实例方法名字的字符串调用方法
'''
我们有三个图形类 Circle,Triangle,Rectangle
他们都有一个获取图形面积的方法,但是方法名字不同,我们可以实现一个统一的获取面积的函数,使用每种方法名进行尝试,调用相应类的接口
'''
class Triangle:
def __init__(self, a, b, c):
self.a, self.b, self.c = a, b, c
def get_area(self):
a, b, c = self.a, self.b, self.c
p = (a + b + c) / 2
return (p * (p - a) * (p - b) * (p - c)) ** 0.5
class Rectangle:
def __init__(self, a, b):
self.a, self.b = a, b
def getArea(self):
return self.a * self.b
class Circle:
def __init__(self, r):
self.r = r
def area(self):
return self.r ** 2 * 3.14159
def get_area_from_shape(shape):
method_name = ['get_area', 'getArea', 'area']
for name in method_name:
f = getattr(shape, name, None)
if f:
return f()
shape1 = Triangle(2, 2, 3)
shape2 = Rectangle(2, 3)
shape3 = Circle(4)
print(get_area_from_shape(shape1))
print(get_area_from_shape(shape2))
print(get_area_from_shape(shape3))
class Triangle:
def __init__(self, a, b, c):
self.a, self.b, self.c = a, b, c
def get_area(self):
a, b, c = self.a, self.b, self.c
p = (a + b + c) / 2
return (p * (p - a) * (p - b) * (p - c)) ** 0.5
class Rectangle:
def __init__(self, a, b):
self.a, self.b = a, b
def getArea(self):
return self.a * self.b
class Circle:
def __init__(self, r):
self.r = r
def area(self):
return self.r ** 2 * 3.14159
def get_area_from_shape(shape):
method_name = ['get_area', 'getArea', 'area']
for name in method_name:
f = getattr(shape, name, None)
if f:
return f()
shape1 = Triangle(2, 2, 3)
shape2 = Rectangle(2, 3)
shape3 = Circle(4)
shape_list = [shape1, shape2, shape3]
area_list = list(map(get_area_from_shape, shape_list))
print(area_list)
-
getattr(x,“y”,None) --> 等同于
x.y
当x中不含有y时,返回None。 -
map(func,iterable) --> 将iterable中的元素一一映射到func函数中处理,并且返回新的map对象。