面向对象
面向对象
面向对象编程
核心是“对象”二字,对象指的是盛放相关数据与功能的容器
基于该思想编写的程序就是在创造一个个容器把相关的东西盛放到一起
优点:扩展性强
缺点:加大了编程的复杂程度
面向对象三大特点:继承、多态、封装
类
一系列对象相似的特征与技能的结合体
程序中是先定义(类),后使用(产生对象)
ps:在程序中特征用变量标识,技能用函数标识,类中最常见的无非是变量函数的定义
类是用来解决对象间代码冗余问题的
类在定义阶段发生的三件事:
- 会执行类体的代码
- 会产生一个类的空间名称,将类体代码运行过程中产生的名字都丢进去
- 将名称空间的内存地址绑定给类名
类在调用阶段发生的三件事:
- 会创建空对象(调用
__new__方法来创建空对象) - 会自动触发类中
__init__函数的运行 - 返回该对象,赋值给变量
类中可以有任意的Python代码,这些代码在定义阶段就会执行,执行后会产生新的名称空间,用来存放类的变量名和函数名,可以通过__dict__方法来查看
对于经典类来说,可以通过该字典操作名称空间的名字(新式类有限),但Python为我们提过了专门的.语法,.是访问属性的语法,类中定义的名字都都是类的属性
类中共用的属性直接定义在类中,而对象独有的属性需要定义在__init__方法中
__init__方法 # 该方法会在对象产生之后自动执行,专门为对象进行初始化操作,可以有任意代码,但一定不能返回非None的值
#python为类内置的特殊属性
类名.__name__ # 类的名字(字符串)
类名.__doc__ # 类的文档字符串
类名.__base__ # 类的第一个父类(在讲继承时会讲)
类名.__bases__ # 类所有父类构成的元组(在讲继承时会讲)
类名.__dict__ # 类的字典属性
类名.__module__ # 类定义所在的模块
类名.__class__ # 实例对应的类(仅新式类中)
绑定方法与非绑定方法
绑定方法:绑定给谁就应该由谁来调用,谁来调用就会将自己当做第一个参数自动传入,默认就是绑定给对象的,加了@classmethod后是绑定给类的
非绑定方法:不与任何人绑定,意味着谁都可以来调用,但无论是是谁来调用就是一个普通函数没有自动传参的效果,需要加@staticmethod
隐藏属性
隐藏属性的目的是为了不让使用者在类外部直接操作属性
可以隐藏数据属性和方法属性,只需在需要隐藏的属性名前加上双下划线
__开头的属性隐藏特点:
- 并不是真正的隐藏,只是一种语法意义上的变形
- 该变形操作只在类定义阶段扫描语法时发生一次,类定义之后__开头的属性不会变形
- 该隐藏对内部对外
隐藏数据属性的目的:为了严格控制某一属性的操作
隐藏方法属性的目的:隔离复杂度
property
一个可以使实例方法用起来像实例属性一样的特殊关键
property属性的定义和调用要注意以下几点:调用时,无需括号,加上就错了;并且仅有一个self参数
用法一,装饰器
- @property 对应读取
- @方法名.setter 修改
- @方法名.deleter 删除属性
class People:
def __init__(self,name,age):
self.__name = name
self.age = age
@property
def name(self):
return "名字:%s" %self.__name
@name.setter
def name(self,v):
if type(v) is not str:
print("名字必须是str类型小垃圾")
return
self.__name = v
@name.deleter
def name(self):
del self.__name
print('删除成功')
obj = People('egon',18)
# print(obj.name)
# obj.name = 123
del obj.name
用法二
property([fget, fset, fdel, doc)
fget -- 获取属性值的函数
fset -- 设置属性值的函数
fdel -- 删除属性值函数
doc -- 属性描述信息
class People:
def __init__(self,name,age):
self.__name = name
self.age = age
def get_name(self):
return "名字:%s" %self.__name
def set_name(self,v):
if type(v) is not str:
print("名字必须是str类型小垃圾")
return
self.__name = v
def del_name(self):
del self.__name
print('删除成功')
name = property(get_name,set_name,del_name)
obj = People('egon',18)
# print(obj.name)
# obj.name = 123
del obj.name
继承
- 什么是继承
- 继承是一种新建子类的方式,新建的类称之为子类或派生类,被继承的类称之为父类或基类
- 子类会继承父类的属性
- 为何要继承
- 类是解决对象之间冗余问题的
- 继承可以解决类与类之间的冗余问题
- 如何继承
- 在Python中支持多继承
- 在Python3中,如果一个类没有任何父类,那么默认继承object类
- 但凡是继承了object类的子类,以及该子类的子子孙孙类都能用到object类的功能,称之为新式类
- 没有继承object类的子类,以及该子类的子子孙孙类都不能用到object类的功能,称之为经典类
- ps:只有在Python2 中区分经典类和新式类,Python3 中都是新式类
# class ParentClass1: #定义父类
# pass
#
# class ParentClass2: #定义父类
# pass
#
# class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass
# pass
#
# class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类
# pass
在子类派生的新方法中如何重用父类的功能:
方式一:指名道姓地访问父类的函数,与继承无关
方式二:super(),严格依赖继承
class People:
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
class Student(People):
def __init__(self, name, age, gender, courses=None):
People.__init__(self, name, age, gender,)
# super().__init__(name, age, gender)
if courses is None:
courses = []
self.courses = courses
class Teacher(People):
def __init__(self, name, age, gender, level):
People.__init__(self, name, age, gender,)
# super().__init__(name, age, gender)
self.level = level
继承的属性查找
单继承:先找对象,再找类,最后去父类,父类一层一层往上,哪里引发的查找就从哪开始找
多继承:
非菱形继承:经典类和新式类属性查找都一样,都是一个一个分支的往下找
菱形继承/死亡钻石:一个类继承的多条分支最终汇聚到一个非object类上
新式类:广度优先,最后找“大脑袋”
经典类:深度优先,在第一条分支就会检索到“大脑袋”
c3会计算出一个mro列表,属性查找参考的是属性查找发起者的mro列表


多继承的Mixin机制
一个类继承多个父类后会产生一些混杂的情况
将类分为主类(主要)和从属类(次要),从属类加不加都不会影响子类的正常使用
一般在从属类加上Mixin, able, ible的后缀
从属类往左放,主类往右放
class Vehicle: # 交通工具
pass
class FlyMixin:
def fly(self):
pass
class CivilAircraft(FlyMixin,Vehicle): # 民航飞机
pass
class Helicopter(FlyMixin,Vehicle): # 直升飞机
pass
class Car(Vehicle): # 汽车
pass
组合
组合是解决类与类之间代码冗余的另一个方法
继承表达的是is-a关系
组合表达的是has-a关系:一个对象的属性值是另外一个对象,一个类的对象可以有另一个类产生的对象作为属性,
比如学生类的对象可以有课程类的对象作为属性
多态
同一种食物有多种形态,具体表现就是多个类继承同一个父类
父类为子类统一一个标准,这些方法需要子类重写
接口约束子类行为(统一标准):
1 鸭子类型--> 不用继承,人为的规定约束类中的方法(python推崇的方法)
class Pig:
def speak(self):
pass
def run(self):
pass
class People:
def speak(self):
pass
def run(self):
pass
class Dog:
def speak(self):
pass
def run(self):
pass
2 abc模块 --> 继承后在子类中给这些方法加上装饰器,不重新这些方法会报错
import abc
class Animal(metaclass=abc.ABCMeta):
@abc.abstractmethod
def speak(self):
pass
@abc.abstractmethod
def run(self):
pass
class Pig(Animal):
def speak(self):
pass
def run(self):
pass
class People(Animal):
def speak(self):
pass
def run(self):
pass
Animal类不能再被使用,只能被继承
3 父类抛异常 --> 在父类的这些方法中直接抛异常,需要重写方法才能运行
def update(self, instance, validated_data):
raise NotImplementedError('`update()` must be implemented.')
def create(self, validated_data):
raise NotImplementedError('`create()` must be implemented.')
内置方法
满足某种条件会自动运行的方法,一般都是双下划线开头,双下划线结尾魔法方法
__getattr__:对象.属性,会触发它的执行
__setattr__:添加/修改属性会触发它的执行
__delattr__:删除属性的时候会触发
__new__:对象创建的时候执行,在__init__之前
__init__:对象初始化的时候执行
__del__:当对象在内存中被释放时,自动触发执行。
__call__:对象后面加括号,触发执行。
__get__:调用一个属性时,触发
__set__:为一个属性赋值时,触发
__delete__:采用del删除属性时,触发
__str__:在打印对象时,需要返回一个字符串
__repr__:改变对象的字符串显示,也是在打印对象显示
自定制格式化字符串__format__
isinstance(obj,type) # 判断obj对象是不是type类
issubclass(obj1,obj2) # 判断obj1是不是obj2的子类
反射
通过字符串来操作类中的方法
hasattr(obj,name) :判断obj中有没有一个name字符串对应的方法或属性
getattr(obj, name, default=None) :获取obj的name字符串对应的方法或属性,default:指定返回值
setattr(obj, k, v) :设置obj中k字符串对应的方法或属性为v
delattr(obj, name) :删除obj中name字符串对应的方法或属性,不存在则报错

浙公网安备 33010602011771号