本周内容概览
- 面向对象
- 面向对象简介
- 类与对象
- 类与对象概念
- 类与对象创建
- 对象独有的数据与方法
- 动静态方法
- 面向对象三大特性
- 三大特性之继承
- 继承的本质
- 名字的查找顺序
- 经典类与新式类
- 派生方法
- 三大特性之封装
- property伪装属性
- 三大特性之多态
- 三大特性之继承
- 面向对象之反射
- 面向对象魔法方法
- 元类
- 产生类的两种方式
- 元类基本使用
- 双下new方法
面向对象
面向对象简介
- 面向对象是一种解决问题的思想,与之相对的就是面向过程编程,之前的代码编写中,一直使用的都是面向过程编程,通过按照流程一步步得到结果,而面向对象则是将数据和功能绑定在一起,创造一个对象,使用一个个对象去实现功能
两种编程思想没有优劣之分,只是使用场景不同,甚至更多时候是两者结合使用
类与对象
类与对象概念
对象:数据与功能的结合体
类:多个对象相同的数据和功能的结合体
类与对象创建
class Myclass:
pass
1.class是创建类的关键字
2.Myclass是类名,类名的命名和变量名一致,并且推荐首字母大写(更有识别度)
3.pass是类体代码公共的数据和方法
类体代码在类定义时就会执行
obj = Myclass()
使用类名加括号即可产生一个对象
查看名称空间的方法: 类/对象名.__dict__
对象独有的数据和方法
1.对象的独有数据:
使用__init__方法在实例化一个对象时添加数据值
class Myclass:
def __init__(self, name, age):
self.name = name
self.age = age
obj = Myclass('jason', 18)
定义一个方法会默认将对象当做第一个值传入,一般使用self接收,后边的参数需要在实例化对象时传入
print(obj.name) # jason
使用对象点名字的方式获取对应的数据值
2.对象的独有方法:
对象真正的独有方法实际上没有办法实现
如果定义在全局中则不是独有的
如果在定义在类中则是公共的
python解释器针对上述问题添加了一个特性
定义在类中的函数默认绑定给对象,相当于就是对象独有的方法
class MyClass:
def __init__(self, name):
self.name = name
def func1(self):
print(self.name)
obj1 = MyClass('jason')
obj2 = MyClass('kevin')
obj1.func1() # jason
obj2.func1() # kevin
使用对象点名字加括号的方式调用方法
动静态方法
在类中定义的方法一共有三种
1.绑定给对象的方法(默认)
2.绑定给类的方法
3.静态方法
class MyClass:
def func1(self): # 实例方法(绑定给对象的方法)
print(self)
@classmethod # 类方法(绑定给类的方法)
def func2(cls):
print(cls)
@staticmethod # 静态方法(就是普通函数)
def func3(a, b):
print(a, b)
obj = MyClass()
MyClass.func1(obj) # 类使用实例方法需要传一个对象
obj.func1() # 对象使用实例方法会自动将自身传入
MyClass.func2() # 类使用类方法会自动将自身传入
obj.func2() # 对象使用类方法会自动将实例化自身的类传入
MyClass.func3(1, 2) # 需要多少参数就传多少
obj.func3(1, 2) # 需要多少参数就传多少
面向对象三大特性之继承
继承就是将被继承类的属性和方法全部获取
继承的目的是为了节省代码的编写
在定义类的时候在类名后加括号填写想要继承的父类
可以继承一个或多个,使用逗号隔开
继承的本质
抽象:将多个类共同的数据或功能提取出来抽象成一个基类
继承:从上往下获取各个基类中的数据与功能
名字的查找顺序
不继承的情况下:先从对象自身开始查找,没有在去产生该对象的类查找,没有的话报错
单继承的情况下:继承一个父类,现充对象自身查找,再去对象的类查找,然后是一个个父类
多继承的情况下:继承多个父类,有两种情况
- 非菱形继承,多个父类最后不会汇总到一个父类,采用深度优先,第一个父类层层向上,最后都没有的话再去第二个父类里查找
- 菱形继承,多个父类最后都是继承的同一个父类,采用广度优先,第一个父类层层向上,但不会走到汇总的那个类,汇总的那个类的所有子类都没有才会在其中查询
使用类点mro()方法查看查找顺序
经典类与新式类
经典类是不继承object或其子类的类
新式类是继承了object或其子类的类
在python3中,默认创建类都继承object,所以python3中没有经典类
在python2中,可以创建经典类和新式类
由于经典类没有核心的功能,所以在python3直接砍掉了
但是在定义类的时候,如果没有想要继承的父类,还是推荐以下写法
class MyClass(object):
pass
虽然和不写是相同的,但是这段代码放在python2中也同样能运行
派生方法
在子类中重写了父类的方法并且扩展了该方法
class MyJsonEncode(json.JSONEncoder):
def default(self, o):
if isinstance(o, datetime.datetime)
return o.strftime(%Y-%m-%d %H:%M:%S)
return super().default(o)
"""重写了JSONEncoder模块,使其遇到datetime类型数据将其格式化为字符串返回"""
面向对象三大特性之封装
封装就是将数据或方法隐藏起来,然后开设一个特定的接口,让用户只能根据这个接口才能使用到封装过的数据或方法
在类的定义阶段,使用双下划线开头的名字,就是隐藏的名字,类的外边无法直接获取,子类也不会继承隐藏属性
但python不会真正的限制任何代码,可以使用__dict__可以看到隐藏的名字只是做了个变形,在名字前加上_类名,不过这样也没有了隐藏的意义
property伪装属性
将方法伪装成数据,让方法不加括号调用(方法不能有参数)
class MyClass(object):
def __init__(self, name):
self.__NAME = name
@property
def name(self):
return self.__NAME
@name.setter
def name(self, value):
if not isinstance(value, str):
raise TypeError('类型错误')
self.__NAME = value
@name.deleter
def name(self):
raise PermissionError('无法删除')
obj = MyClass('jason')
print(obj.name)
obj.name = 'kevin'
del obj.name
"""
使用@property语法糖伪装方法
使用@伪装方法名.setter添加伪装函数的修改功能
使用@伪装方法名.deleter添加伪装函数的删除功能
"""
面向对象三大特性之多态
多态:一种事物的多种形态
一种事物有多种形态,但是多种形态的相同功能应该使用相同的名字
也可以使用abc模块设置一个抽象类,类中装饰方法使其继承当前类的子类都必须重写此方法
import abc
class MyClass(metaclass=abc.ABCMeta):
@abc.abstractmethod # 使用装饰器限制子类必须要有一个func的方法
def func(self):
pass
鸭子类型:只要你长得像鸭子,走路像鸭子,说话像鸭子,那么你就是鸭子
面向对象之反射
反射:通过字符串来操作对象的属性或方法
反射主要使用四个方法:
- hasattr(): 根据字符串判断对象是否存在指定的属性或方法
- getattr(): 根据字符串获取对象内指定的属性或方法
- setattr(): 根据字符串设置对象的属性
- delattr(): 根据字符串删除对象的属性
class MyClass(object):
def __init__(self, name):
self.name = name
def func(self):
print('func')
obj = MyClass('xm')
print(hasattr(obj, 'name')) # True
print(hasattr(obj, 'func')) # True
print(hasattr(obj, 'xxxx')) # False
print(getattr(obj, 'name')) # xm
getattr(obj, 'func')() # func
setattr(obj, 'name', 'jason')
setattr(obj, 'age', 18)
print(obj.__dict__) # {'name': 'jason', 'age': 18}
delattr(obj, 'name')
print(obj.__dict__) # {'age': 18}
面向对象魔法方法
魔法方法就是在类中定义的双下方法,魔法方法会在特定条件下自动触发
"""常用的魔法方法"""
class MyClass(object):
def __init__(self): # 实例化对象时自动触发
pass
def __str__(self): # 在对象被打印是自动触发,需要返回一个字符串以供打印
return ''
def __call__(self,*args, **kwargs): # 对象被加括号调用时自动触发
pass
def __getattr__(self, item): # 获取一个对象中不存在的数据时自动触发,返回值就是外界获取的值
return 'item'
def __setattr__(self, key, value): # 操作对象的属性时自动触发 super().__setattr__(key, value)
def __del__(self): # 对象被主动或被动删除时自动触发
pass
def __getattribute__(self, item): # 对象获取属性时自动触发,无论属性是否存在,此方法相比__getattr__优先级更高
return super().__getattribute__(item)
def __enter__(self): # 对象被with语法执行时自动触发,返回值会被as后的变量名接收
pass
def __exit__(self, exc_type, exc_val, exc_tb): # 对象被with语法执行结束时自动触发
pass
元类
使用type可以查看对象的所属类是谁,使用type查询类的所属类可以发现是被type创建的
这种创建类的类就被称为元类
产生类的两种方式
- 使用class关键字创建类
- 使用元类
type(类名, 父类, 名称空间)
元类基本使用
继承了type的类才可以称为元类
想要切换产生类的元类不能使用继承必须使用关键字metaclass声明
可以在元类中使用__init__控制类的创建,在元类中使用__call__控制对象的创建
class MyMetaClass(type):
def __init__(self,what, bases=None, dict=None):
if not what.istitle(): # 控制创建类不许第一个字母大写其他字母小写
raise Exception('首字母必须大写')
super().__init__(what, bases, dict)
def __call__(self, *args, **kwargs):
if args: # 控制创建对象只能用关键字传参
raise Exception('必须使用关键字传参')
super().__call__(*args, **kwargs)
class MyClass(metaclass=MyMetaClass):
pass
双下new方法
创建对象的步骤
- 创建一个空对象
- 触发__init__方法实例化对象
- 返回对象
__new__方法的作用就是创建一个空对象