面向对象
6. 面向对象
[TOC]
面向对象和面向过程编程
面向对象编程是一个编程思想,python中一切皆对象
'''
1. 面向对象编程
- 核心是’对象‘二字,对象指的是特征和技能的结合体
- 基于该编程思想编写程序,就好比在创造世界,一种上帝式的思维方式
- 优点:可扩展性高
- 缺点:编写程序的复杂程度要远高于面向过程编程思想
2. 面向过程编程
- 核心是'过程‘二字,过程指的是做事情的步骤,比如先做什么,在做什么
- 基于该编程思想编写程序,就好比一条工厂的流水线,一种机械式的思维方式
- 优点:逻辑清晰,复杂的问题流程化,进而简单化
- 缺点:可扩展性差
'''
类和对象
调用类时自动触发,为不同的对象初始化定制属性
- '''
1. 定义
- 类是一系列具有相同的属性和方法的对象的集合体,一般使用“驼峰体”命名
- 对象是属性和方法的结合体,调用类即会产生对象,这个过程也叫做类的实例化,产生的对象称之为类的一个实例
2. 名称空间
- 类的名称空间在定义阶段产生,执行python文件时会执行类内部的代码
- 对象的名称空间在调用类的时候产生
3. 属性访问
- 对象存放各自独有的属性,类中存放对象们共有的内容
- 对象和类均可以通过.获取属性或方法
- 对象可以获取自己和类的属性,类只能获取类属性
4.对象绑定方法
- 由类调用类内部的函数,该函数只是一个普通的函数,普通函数需要接受几个参数,就得传入几个参数
- 由对象来调用称之为对象的绑定方法,不同的对象调用该绑定方法,则会将不同的对象传入该绑定方法中,即self参数
5. __init__方法
- 调用类时自动触发,为不同的对象初始化定制对象属性
- 没有该方法,实例化时可以不传参数
6. __dict__方法,
- 查看名称空间内的所有名字,即内部的方法和属性
- 对象和类均有该方法
'''
class Student:
school = '清华大学'
# 该方法会在对象产生之后自动执行,专门为对象进行初始化操作,可以有任意代码,但一定不能返回非None的值
def __init__(self, name, sex, age):
self.name = name
self.sex = sex
self.age = age
def choose(self):
print('%s is choosing a course' % self.name)
stu1 = Student('李建刚', '男', 28) # 类的实例化
stu2 = Student('王大力', '女', 18)
stu3 = Student('牛嗷嗷', '男', 38)
print(stu1.name) # 李建刚
stu1.choose() # 李建刚 is choosing a course
print(Student.__dict__)
print(stu1.__dict__) # {'name': '李建刚', 'sex': '男', 'age': 28}
面向对象的三大特性
封装
'''
1. 什么是封装
- 封装指的是数据和功能整合到一起,隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别
- 把一堆属性封装到一个对象中,对象可以通过"."的方式获取属性,从而方便存取
2. 访问限制机制(隐藏属性)
- 在类内部凡是以__开头的属性都会被隐藏,外部不能直接访问该属性,只有类内部可以直接访问
- 该属性其实只是做了一个变形,检测语法时自动变成"_类名__属性",在类外部可以通过该方法访问
- 一般会通过提供相应的接口让外部访问该属性(通过函数返回)
3. property
- python内置装饰器,可以将类内部的函数变成数据属性,从而在调用的时候不用加括号
- 被装饰过的属性不能被修改,如果非要修改可以用装饰器中的setter和deleter进行删改
'''
class Foo:
__N = 0 # 变形为_Foo__N
def __init__(self):
self.__x = 10 # 变形为self._Foo__x
# 开放接口,外界可以通过该方法获取_N值
@property
def f1(self):
return self.__N
obj = Foo()
print(obj.f1) # 0,不用加括号调用,获取内部隐藏属性
print(obj._Foo__x) # 10 # 外部可通过_类名__属性访问
print(obj.__x) # 报错
print(Foo._Foo__N) # 0
print(Foo.__N) # 报错
继承
'''
1. 什么是继承
- 继承是一种创建新类的方式,是一系列类相同的特征与技能的结合体
- 新建的类称之为子类/派生类,子类继承的类叫做父类/基类/超类
- 子类可以继承父类的属性,并且可以派生出自己的属性,从而减少代码的冗余
注意:在python中,一个子类可以有多个父类,其他语言子类只有一个父类
2. 怎么样继承
- 先抽象(总结相似之处),再继承
3. 属性查找 (mro()方法可以查询当前类的继承顺序)
- 先从对象自己的__dict__中找,如果没有则去类中找,然后再去父类中找
4. 派生与方法重用
子类派生自己新的属性,在进行属性查找时,子类中的属性名会优先于父类被查找,因此需要重用父类方法
- 父类函数调用
直接通过父类.__init__,把__init__当作普通函数使用,传入对象和继承的属性
- super()
super是一个特殊的类,在子类中调用super()会得到一个特殊的对象,通过'.'指向父类的名称空间
5. 新式类和经典类
- 新式类:继承object的类以及其子类就叫新式类
- python3中所有的类默认继承object类,即所有类都是新式类
- python2中需要手动继承
- 经典类:没有继承object的类以及其子类,就叫经典类
- python3中不存在经典类
- 只有python2中才有经典类
6. 钻石继承
在多继承的情况下形成的钻石继承 (继承顺序),经典类与新式类会有不同MRO(方法解析顺序)
- 经典类:深度优先(当查找属性不存在时的查找顺序)
- 新式类:广度优先(当查找属性不存在时的查找顺序)
7. 组合
- 组合指的是在一个类中以另外一个类的对象作为数据属性,是类和对象的关系
'''
class People:
def __init__(self,name,sex,age):
self.name=name
self.sex=sex
self.age=age
# 方法重用方式1:父类函数调用
class Teacher(People):
def __init__(self,name,sex,age,title):
People.__init__(self,name,age,sex) #调用的是函数,因而需要传入self
self.title=title
# 方法重用方式2:super()
class Teacher(People):
def __init__(self,name,sex,age,title):
super().__init__(name,age,sex) #调用的是绑定方法,自动传入self
self.title=title


多态
'''
1. 多态和多态性
- 多态:指一类事物有多种形态,(一个抽象类有多个子类,因而多态的概念依赖于继承)
- 多态性:指具有不同功能的函数可以使用相同的函数名,同一种调用方式,会有不同的执行效果
2. 目的
- 让不同类型的对象,调用同一个方法名去执行不同的功能
- 父类:定义要统一的标准
- 子类:遵循父类的标准
- 最终目的:统一子类编写的规范,让使用者可以更方便的调用相同功能的方法
3. 实现方式
- 继承
- 抽象类
4. 抽象类(abc模块,强制子类必须遵循父类的标准)
- 定义类时,设置参数metaclass=abc.ABCMeta
- 定义函数时,加上装饰器 @abc.abstractmethod
- 这样继承该类的子类中必须实现被装饰的方法名
5. 鸭子类型
- “如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子”
- 优点:鸭子类型相较于继承,耦合度低,扩展型强
注意:在python中,不推荐使用抽象类强制限制子类的的定义,但是推荐类都遵循鸭子类型
'''
# 抽象类
import abc
class Animal(metaclass=abc.ABCMeta):
# 吃
@abc.abstractmethod
def eat(self):
pass
# 喝
@abc.abstractmethod
def drink(self):
pass
# 叫
@abc.abstractmethod
def speak(self):
pass
# 猪
class Pig(Animal):
# 吃
def eat(self):
print('猪在吃饭')
pass
# 喝
def drink(self):
pass
def speak(self):
print('哼哼哼~~~')
# 派生
def run(self):
pass
pig = Pig()
绑定方法和非绑定方法
'''
1. 绑定方法
- 对象绑定方法:类中正常定义的函数默认是绑定到对象的,对象调用时会将自身当做第一个参数传入
- 类的绑定方法:加上装饰器@classmethod的函数,类调用时会将自身当做第一个参数传入
2. 非绑定方法:类中的加上装饰器@staticmethod后的函数,也成为静态方法
- 该方法不与类或对象绑定,类与对象都可以来调用它,但它就是一个普通函数而已,不会自动传值
'''
class People:
# 类的绑定方法
@classmethod
def tell_info(cls):
print(f'我是类{cls}的绑定方法')
@staticmethod
def create_id():
print('我是静态方法')
People.tell_info() # 我是类People的绑定方法
People.create_id() # 我是静态方法
反射
'''
1. 什么是反射
- 在Python中,反射指的是通过字符串来操作对象的属性,涉及到四个内置函数的使用
2. 反射的函数
- hasattr:通过字符串判断对象或类是否存在该属性
- getattr:通过字符串,获取对象或类的属性
- setattr:通过字符串,设置对象或类的属性
- delattr:通过字符串,删除对象或类的属性
'''
class People:
country = 'China'
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
p = People('wick',24,'man')
# hasattr
print(hasattr(p,'name')) # True
print('name' in p.__dict__) # True
# getattr
print(getattr(p,'name','tank')) # wick
print(p.__dict__.get('name')) # wick
# setattr
setattr(p,'sal','3.0')
print(hasattr(p,'sal')) # True
# delattr
delattr(p,'sal') # 相当于del p.level
print(hasattr(p,'sal')) # False
类的内置方法(魔法方法)
'''
1. 类的内置方法
- 凡是在类内部定义,以__开头和__结尾的方法都称之为魔法方法
- 魔法方法会在某些条件成立时触发
2. 有哪些内置方法
__init__:在调用类时触发
__str__:会在打印对象时触发,必须要返回值,必须是字符串类型
__del__:对象被销毁前执行该方法
__delattr__:删除属性的时候触发该方法
__getattr__:会在对象.属性时,属性没有才会触发
__setattr__:会在对象.属性 = 属性时触发
__getattribute__:对象.属性时,不管属性有没有都会触发,和__getattr__共存时,只会触发该方法
__call__:会在对象被调用时触发
__new__:会在__init__执行前触发
__enter__:出现with语句触发,有返回值则赋值给as声明的变量
__exit__:with中代码块执行完毕时执行
__doc__:可以获取类的描述信息(注释)
__module__:获取当前操作对象在哪个模块
__class__:获取当前对象的类
'''
class Foo(object):
def __new__(cls, *args, **kwargs):
print(cls)
return object.__new__(cls,*args, **kwargs) # 真正产生一个空对象
# 若当前类的__new__没有return一个空对象时,则不会触发。
def __init__(self):
print('在调用类时触发...')
def __str__(self):
print('会在打印对象时触发...')
# 必须要有一个返回值, 该返回值必须是字符串类型
return '[1, 2, 3]'
def __del__(self):
print('对象被销毁前执行该方法...')
def __getattr__(self, item):
print('会在对象.属性时,“属性没有”的情况下才会触发...')
print(item)
# 默认返回None, 若想打印属性的结果,必须return一个值
return 111
# 注意: 执行该方法时,外部“对象.属性=属性值”时无效。
def __setattr__(self, key, value):
print('会在 “对象.属性 = 属性值” 时触发...')
print(key, value)
print(type(self))
print(self, 111)
self.__dict__[key] = value
def __call__(self, *args, **kwargs):
print(self)
print('调用对象时触发该方法...')
单例模式
'''
1. 单例模式指的是单个实例,实例指的是调用类产生的对象
- 实例化多个对象会产生不同的内存地址,单例可以让所有调用者在调用类产生对象时都指向同一个内存地址,比如打开文件
- 目的:减轻内存的压力
2. 实现单例的几个方法
- __new__方法中做判断
- classmethod:通过类的绑定方法进行实例化
- 装饰器
- import
- 共享属性
'''
# 1. __new__
class A:
def __new__(cls,*args,**kwargs):
if not hasattr(cls,'_instance'):
cls._instance =object.__new__(cls)
return cls._instance
# 2. classmethod
class A:
@classmethod
def singleton(cls,*args,**kwargs):
if not hasattr(cls,'_instance'):
cls._instance = cls(*args,**kwargs)
return cls._instance
for i in range(3):
a = A.singleton()
print(id(a))
# 3. 装饰器
def singleton(cls):
def inner(*args,**kwargs):
if not hasattr(cls,"_instance"):
cls._instance = cls(*args,**kwargs)
return cls._instance
return inner
@singleton
class A:
pass
# 4. import
# index.py
class A:
pass
a = A()
# test.py
from index import a
for i in range(3):
print(id(a))
# 5. 共享属性
class A(object):
c = 1
def __new__(cls, *args, **kwargs):
ob = super().__new__(cls, *args, **kwargs)
ob.__dict__ = {}
return ob
for i in range(3):
a = A()
print(id(a.c))
元类
'''
1. 什么是元类(类的类)
- python中一切皆对象(包括类),负责产生这个类的类就叫做元类
- class创建类时,内部会自动调用元类type创建类,因此继承了type的类就是一个元类
- 作用:控制类的创建
2. class关键字创建类的流程分析
class创建类时,内部会自动调用元类type,type会帮我们创建一个自定义类,此时会传入三个参数:
- 类名:class_name
- 类的基类们:class_bases = (object,)
- 类的名称空间:class_dic,由执行类体代码得到的
3. 自定义元类控制类的创建
- 自定义一个继承了type的元类
- __init__方法中控制类的创建
- __call__方法中模拟type元类调用__new__生成空对象
- 调用__new__产生空对象
- 调用__init__方法初始化对象obj
- 返回初始化的对象obj
- 定义类时通过metaclass参数指定好元类,从而控制类的创建
'''
class MyMeta(type):
# 控制对象的创建
# def __new__(cls,class_name,class_base,class_dic)
# 控制类的定义
def __init__(self,class_name,class_base,class_dic):
# 判断首字母是否大写
if not class_name.istitle():
raise TypeError ('类的首字母必须大写')
# 控制类中必须要有注释
if not class_dic.get('__doc__'):
raise TypeError('类内部必须要写注释')
# 模拟type元类内部做的事情
def __call__(self,*args,**kwargs):
# 1. 调用__new__创建一个空对象
obj = object.__new__(self)
# 2. 执行__init__(self,*args,**kwargs)
obj.__init__(*args,**kwargs)
return obj
class Foo(object,metaclass=MyMeta):
'''注释'''
x= 10
def __init__(self,a,b):
self.a = a
self.b = b
def f1(self):
print('f1')
foo = Foo(10,20) # 调用Foo对象,会触发__call__
print(foo.b)
isinstance和issubclass
'''
1. isinstance(obj,cls):
- 检查obj是否是类 cls 的对象
2. issubclass(sub, super)
- 检查sub类是否是super类的子类
'''
class Foo:
pass
class Goo:
pass
foo_obj = Foo()
print(isinstance(foo_obj,Foo)) # True
print(isinstance(foo_obj,Goo)) # False
print(issubclass(Goo,Foo)) # False
浙公网安备 33010602011771号