面向对象OOP
面向对象
什么是面向对象:
是一种编程思想,核心是对象,程序就是一系列对象的集合,程序员负责调度控制这些对象来交互着完成任务:
案例: 西天取经
如来有一堆破书要传出去,他没有自己干,而是找了五个对象帮他去干
如来只要负责控制调度的对象即可
如果某个对象发生变化,也不会影响其他的对象,扩展性
类和对象
类和对象
# 定义类(类名见名知意,大驼峰命名法,如: Class_Name)
class Person:
pass
# 创建对象
p = Person()
属性
"""
属性可以写在类中: 是所有对象公共的
也可以写在对象中: 是每个对象独有的
如果类中和对象中存在同一属性,先访问对象,再访问类
"""
# 属性的增删改查
class Person:
"""这是一个人的属性"""
desc = "都会吃饭"
p = Person()
# 增加属性: 对象.属性 = 值
p.name = "李四"
print(p.desc, p.name) # 都会吃饭 李四
# 删除属性: del 对象.属性
del p.name
# 修改属性: 对象.属性 = 新的值
p.desc = "喝水"
print(p.desc) # 喝水
# 查看属性: 对象.__dict__ 查看的是对象的所有属性而不包含类的
print(p.__dict__) # {'desc': '喝水'}
# 查看对象的类信息: 对象.__class__
print(p.__class__) # <class '__main__.Person'>
# 查看对象的类名称: 对象.__class__.__name__
print(p.__class__.__name) # Person
# 查看类的名称:类.__name__
print(Person.__name__) # Person
__init__方法
"""
__init__方法: 初始化方法,本质上就是一个函数
当实例化对象时,会自动执行__init__方法
会自动将对象作为第一个参数传入,参数名称为self(不建议改)
用于给对象赋初始值
返回值只能是默认的None
"""
# __init初始化方法可以给对象赋初始化值和执行初始化操作
class Dog:
def __init__(self,kind,color):
print(self) # <__main__.Dog object at 0x000001BA8B00BBE0>
self.kind = kind
self.color = color
# 简便写法
# los = locals()
# los.pop("self")
# self.__dict__.update(lcs)
print("赋值以外的初始化操作")
d1 = Dog("二哈","黑白")
print(d1,d1.name,d1.age) # <__main__.Dog object at 0x000001BA8B00BBE0> 二哈 黑白
绑定方法和非绑定方法
对象绑定方法
"""
默认情况下类中的方法都是对象绑定方法
特殊之处:
当使用对象调用该函数时会自动传入对象本身,作为该函数第一个参数
当使用类来调用该函数时就是一个普通函数,有几个参数就得传几个参数
"""
class Student:
def __init__(self,name):
self.name = name
def say_hi(self):
print("hello i am a student! my name: %s" % self.name)
stu1 = Student("jack",20,"male")
stu1.say_hi() # hello i am a student! my name: jack
Student.say_hi(stu1) # hello i am a student! my name: jack
print(type(stu1.say_hi)) # <class 'method'>
print(type(Student.say_hi)) # <class 'function'>
类绑定方法
"""
类绑定方法用 @classmethod 来装饰
特殊之处: 不管用类还是对象调用,都会自动传入类本身,作为第一个参数
什么时候用对象绑定方法: 当函数逻辑需要访问对象中的数据时
什么时候用类绑定方法: 当函数逻辑需要访问类中的数据时
"""
class OldBoyStudent:
def __init__(self,name):
self.name = name
@classmethod # 类绑定方法,此时cls为类而不为对象
def show_school(cls):
print(cls)
OldBoyStudent.show_school() # <class '__main__.OldBoyStudent'>
print(OldBoyStudent) # <class '__main__.OldBoyStudent'>
非绑定方法
# 或叫静态方法,即不需访问类的数据,也不需要访问对象的数据,不常用
# 语法: @staticmethod
"""
示例: 为学生类添加一个save方法,一个get方法
save: 将对象存储到文件中
get: 从文件中获取对象
"""
import pickle
class Student:
def __init__(self,name):
self.name = name
def save(self):
with open(self.name,"wb") as f:
pickle.dump(self,f)
@staticmethod
def get(name):
with open(name,"rb") as f:
obj = pickle.load(f)
return obj
stu = Student("rose")
stu.save()
obj1 = stu.get("rose")
print(obj1.name) # rose
obj2 = Student.get("rose")
print(obj2.name) # rose
继承
继承语法
"""
a继承了b则a就能直接使用b的方法和属性
a称之为子类,b称之为父类或基类
在python中一个子类可以同时继承多个父类
"""
class Base:
desc = "这是一个基类"
def show_info(self):
print(self.desc)
def make_money(self):
print("一天赚一个亿...")
# 指定父类为Base
class SubClass(Base):
pass
obj = SubClass()
# 即使子类中什么都没有也可以使用父类中已有的内容
print(obj.desc) # 这是一个基类
obj.make_money() # 一天赚一个亿...
抽象
"""
将多个子类中相同的部分进行抽取,形成一个新的类,这个过程称之为抽象
正确使用继承: 先抽象在继承
"""
# 抽取老师和学生中相同的部分形成person类
class Person:
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
def say_hi(self):
print("name: %s,age: %s,gender: %s" % (self.name,self.age,self.gender))
class Teacher(Person):
def teaching(self):
print("老师教学生写代码...")
class Student(Person):
pass
t1 = Teacher("jack",20,"male")
t1.say_hi()
stu1 = Student("rose",18,"female")
stu1.say_hi()
属性的查找顺序
对象自己的 > 所在类中 > 父类 > 父类的父类 > ... > Object
派生和覆盖
"""
派生:
当子类中出现了与父类中不同的内容时,这个子类就称之为派生类
通常子类都会写一些新的代码,不可能与父类完全一样,既通常都是派生类,所以派生类指的就是子类
覆盖: 也称之为重写,当子类出现了与父类名称完全一致的属性或方法
"""
子类中访问父类的内容
"""
方式1: super(当前类名称,self).要调的父类的属性或方法
方式2: super().要调的父类的属性或方法 # py3的新语法,最常用的方式
方式3: 类名称.要调的父类的属性或方法(self) # 方式3与继承无关,直接指定类名调用
当继承一个现有的类并且覆盖了父类的__init__方法时,必须在__init__方法的第一行调用父类的__init__方法,并传入父类所需的参数
"""
class Parent:
text = "abc"
def say_something(self):
print("anything")
class Sub(Parent):
def show_info(self):
# 方式一:
# print(super(Sub,self).text)
# super(Sub,self).say_something()
# 方式二:
# print(super().text)
# super().say_something()
# 方式三:
print(Parent.text)
Parent.say_something(self)
s = Sub() # abc
s.show_info() # anything
class Person:
def __init__(self,name,gender,age):
self.name = name
self.gender = gender
self.age = age
def say_hi(self):
print("name: %s, gender: %s, age: %s" % (self.name,self.gender,self.age))
class Student(Person):
def __init__(self,name,gender,age,number):
super().__init__(name,gender,age)
self.number = number
def say_hi(self):
super().say_hi()
print("numnber: %s" % self.number)
stu = Student("rose","mael",20,"boy01")
stu.say_hi()
# 实现一个能够限制元素类型的列表类
class MyList(list):
def __init__(self,element_type):
super().__init__() # 调用父类的初始化方法来完成基本初始化
self.element_type = element_type
def append(self, object):
"""
:param object: 要存储的元素
:return: 没有
"""
if type(object) == self.element_type: # 当列表要添加的元素的类型和指定的类型相同时
# 这里访问父类的append函数来完成真正的存储操作
super(MyList,self).append(object)
# super().append(object)
# MyList.append(self,object) # RecursionError: maximum recursion depth exceeded while calling a Python object
else:
print("sorry, your element type not is %s" % self.element_type)
m = MyList(int) # 创建对象时指定要存储的元素类型
m.append(1)
print(m[0]) # 1
m.append("111") # sorry, your element type not is int
组合
"""
将一个对象作为另一个对象的属性(既什么有什么)
什么时候用组合: 如果两个类之间完全不属于同类,但需要满足什么有什么
"""
class Phone:
def __init__(self, kind, color):
self.kind = kind
self.color = color
def call(self):
print("正在呼叫XXX...")
class Student:
def __init__(self, name, phone):
self.name = name
self.phone = phone
phone1 = Phone("apple", "red")
stu1 = Student("rose", phone1)
stu1.phone1.call()
菱形继承
"""
python支持多继承
python3中所有类都直接或间接继承了Object
新式类与经典类:
新式类: 任何显式或隐式的继承自object类就称之为新式类,python3中都是新式类
经典类: 即不是Object的子类,仅在python2中出现
菱形继承时:
新式类: 先深度,当遇到了共同父类时就广度
金典类: 就是深度优先
"""
class A:
pass
class B:
pass
class Test(A,B):
pass
print(Test.mro()) # [<class '__main__.Test'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
# 菱形继承
class B:
num = 2
pass
class C:
num = 3
pass
class E(B):
num = 5
pass
class F(C):
num = 6
pass
class G(C):
num = 7
pass
class H(E,F,G):
# num = 8
pass
print(H.num)
print(H.mro()) # [<class '__main__.H'>, <class '__main__.E'>, <class '__main__.B'>, <class '__main__.F'>, <class '__main__.G'>, <class '__main__.C'>, <class 'object'>]
封装
封装语法
"""
什么是封装: 对外隐藏内部实现细节,并提供访问的接口
什么时候应该封装: 当有一些数据不希望外界可以直接修改时,当有一些函数不希望给外界使用时
被封装内容的特点: 外界不能直接访问,内部依然可以使用
权限: 封装可以控制属性的权限,在python只有两种权限
公开的: 默认就是公开的
私有的: 只能由当前类自己使用
语法: 将要封装的属性或方法名称前加上双下划线
python实现封装的原理:
在加载类的时候,把__替换成了_类名__,替换变量名称
"""
# 封装属性
class Person:
def __init__(self, id_number, name, age):
self.__id_number = id_number # 双下划线封装后则不能修改内部的值
self.name = name
self.age = age
def show_id(self):
print(self.__id_number)
p = Person("123456789", "jack", 29)
# print(p.__id_number) # AttributeError: 'Person' object has no attribute '__id_number'
p.__id_number = "123"
print(p.__id_number) # 123
p.show_id() # 123456789
# 封装方法
class PC:
def __init__(self,kind,color):
self.kind = kind
self.color = color
def open(self):
print("接通电源")
self.__check_device()
print("初始化内核")
self.__start_services()
print("启动GUI")
def __check_device(self):
print("硬件检测")
def __start_services(self):
print("启动服务")
pc1 = PC("苹果","红色")
pc1.open()
pc1.__check_device() # AttributeError: 'PC' object has no attribute '__check_device'
# python实现封装的原理
class A:
def __init__(self,key):
self.__key = key
def key(self):
return self.__key
a = A("123")
print(a.__key) # AttributeError: 'A' object has no attribute '__key'
print(a.__dict__) # {'_A__key': '123'}
print(a._A__key) # 123
a.__key = 1
print(a.__dict__) # {'_A__key': '123', '__key': 1}
外界访问私有内容
# 通过定义方法来完成对私有属性的访问和修改,则可以在外界修改这个关键数据时做一些限制
class Downloader:
def __init__(self,url,buffer_size):
self.url = url
self.__buffer_size = buffer_size
def start_download(self):
if self.__buffer_size <= 1024*1024:
print("开始下载...")
print("当前缓冲器大小: ",self.__buffer_size)
else:
print("内存炸了...")
def set_buffer_size(self,size):
# 定义方法来完成对私有属性的访问和修改
if not type(size) == int:
print("缓冲区大小必须是整型...")
else:
self.__buffer_size = size
print("缓冲区大小修改成功...")
def get_buffer_size(self):
return self.__buffer_size
d = Downloader("http: //www.baicu.com",1024*1024)
# 通过函数去修改内部封装的属性
d.set_buffer_size(1024*2048)
# 通过函数访问内部封装的属性
print(d.get_buffer_size())
d.start_download()
property装饰器
"""
通过定义方法来完成对私有属性的访问和修改,给对象的使用者带来了麻烦,必须知道哪些是普通属性哪些是私有属性,需要使用不同的方式来调用他们,property装饰器就是为了使得调用方式一致
使用property装饰器可以将私有属性伪装成普通属性,使得私有属性调用方法跟普通属性一致
property: 用在获取属性的方法上
@属性名.setter: 用在修改属性的方法上
@属性名.deleter: 用在删除属性的方法上
注意: 内部会创建一个对象,对象名称为属性名称,所以在使用setter和deleter时,必须使用对象名称去调用方法
property装饰器还可以用来实现计算属性
计算属性: 属性的值不能直接获得,必须通过计算才能获取,例如: 正方形求面积
"""
class A:
def __init__(self,name,key):
self.name = name
self.__key = key
@property
def key(self):
return self.__key
@key.setter
def key(self,new_key):
if new_key <= 100:
self.__key = new_key
else:
print("key必须小于等于100")
@key.deleter
def key(self):
del self.__key
a = A("jack",123)
print(a.key) # 123
a.key = 99
print(a.key) # 99
del a.key
print(a.key) # AttributeError: 'A' object has no attribute '_A__key'
# 问题
class Square:
def __init__(self,width):
self.width = width
self.area = self.width * self.width
s = Square(10)
print(s.area) # 100
s.width = 2
print(s.area) # 100
# property装饰器实现计算属性
class Square:
def __init__(self,width):
self.width = width
@property
def area(self):
return self.width * self.width
s = Square(10)
print(s.area) # 100
s.width = 2
print(s.area) # 4
接口
"""
接口是一组功能的集合,接口中仅包含功能的名字,不包含具体的实现代码
接口本质是一套协议标准,遵循这个标准的对象就能被调用
接口方便了对象的使用者,只要学习一套使用方法,就可以以不变应万变
问题: 如果子类没有按照协议来设计,也没办法限制他,将导致代码无法运行
"""
# 案例: 电脑提前制定一套USB接口协议,PC的代码一旦完成,后期无论什么样的设备,只要遵循了USB接口协议,都能够被电脑所调用
class USB:
def open(self):
pass
def read(self):
pass
def write(self):
pass
def close(self):
pass
def pc(usb_device):
usb_device.open()
usb_device.read()
usb_device.write()
usb_device.close()
class Mouse(USB):
def open(self):
print("鼠标开机...")
def read(self):
print("获取光标位置...")
def write(self):
print("鼠标不支持写入...")
def close(self):
print("鼠标关机...")
class KeyBoard(USB):
def open(self):
print("键盘开机...")
def read(self):
print("获取按键字符...")
def write(self):
print("键盘敲入代码...")
def close(self):
print("键盘关机...")
# 鼠标对象
m = Mouse()
# 将鼠标传给电脑
pc(m)
# 键盘对象
k = KeyBoard()
# 将键盘传给电脑
pc(k)
鸭子类型
"""
python一般不会限制你必须怎么写,但作为一个优秀的程序员,就应该自觉遵守相关协议,所以有了鸭子类型这么一说
只要保证你的类按照相关协议类编写,也可以达到提高扩展性的目的
接口是一套协议规范,明确子类应该具备哪些功能
抽象类是用于强制要求子类必须按照协议中规定的来实现
然而python不推崇限制你的语法,可以设计成鸭子类型,既让多个不同类对象具备相同的属性和方法,对于使用者而言,轻松的使用各种对象
"""
def pc(usb_device):
usb_device.open()
usb_device.read()
usb_device.write()
usb_device.close()
class Mouse():
def open(self):
print("鼠标开机...")
def read(self):
print("获取光标位置...")
def write(self):
print("鼠标不支持写入...")
def close(self):
print("鼠标关机...")
class KeyBoard():
def open(self):
print("键盘开机...")
def read(self):
print("获取按键字符...")
def write(self):
print("键盘敲入代码...")
def close(self):
print("键盘关机...")
# 鼠标对象
m = Mouse()
# 将鼠标传给电脑
pc(m)
# 键盘对象
k = KeyBoard()
# 将键盘传给电脑
pc(k)
抽象类
"""
抽象类(abstract class): 包含抽象方法(没有函数体的方法)的类,
作用: 可以限制子类必须有父类中定义的抽象方法
"""
import abc
# 子类必须要定义run方法,否则会报错
class AClass(metaclass=abc.ABCMeta):
@abc.abstractmethod
def run(self):
pass
class B(AClass):
def run(self):
print("run...")
b = B()
多态
"""
概念: 多个不同类对象可以响应同一个方法,产生不同的结果,既多个不同类对象有相同的使用方法,
实现多态: 接口、抽象类、鸭子类型都可以写出具备多态的代码
"""
# 案例:
class Ji:
def bark(self):
print("咯咯咯")
def spawn(self):
print("下鸡蛋...")
class Ya:
def bark(self):
print("嘎嘎嘎")
def spawn(self):
print("下鸭蛋...")
class E:
def bark(self):
print("饿饿饿")
def spawn(self):
print("下鹅蛋...")
j = Ji()
y = Ya()
e = E()
def manage(obj):
obj.spawn()
manage(j)
manage(y)
manage(e)
# python中到处都有多态
a = 10
b = "10"
c = [10]
print(type(a))
print(type(b))
print(type(c))
OOP相关内置函数
#1. isinstance(对象,类型) 判断对象是否是某个类的实例
def add_num(a,b):
if isinstance(a,int) and isinstance(b,int):
return a+b
return None
print(add_num(20,10))
#2. issubclass(type(子类),父类) 判断一个类是否是另一个类的子类
参数一是子类
参数二是父类
class Animal:
def eat(self):
print("动物得吃东西...")
class Pig(Animal):
def eat(self):
print("猪得吃猪食...")
class Tree:
def light(self):
print("植物光合作用...")
def manage(obj):
if issubclass(type(obj),Animal):
obj.eat()
else:
print("不是动物!")
pig = Pig()
tree = Tree()
manage(pig)
manage(tree)
类中的魔法函数
__str__
"""
执行时机: 在对象被转换为字符串时执行,转换的结果就是__str__函数的返回值
使用场景: 自定义对象的打印格式(print打印会转成字符串,所以会触发__str__)
"""
class Person:
def __str__(self):
print("run")
return "person"
p = Person()
print(p) # run person
str(p) # run
print(str(p)) # run person
__del__
"""
执行时机: 手动删除对象时立即执行,或是程序运行结束时也会自动执行
使用场景: 当对象在使用过程中,打开了不属于解释器的资源,例如: 文件,网络端口
"""
class Person:
def __del__(self):
print("del run")
p = Person()
del p # del run
# 案例:
class FileTool:
"""该类用于简化文件的读写操作 """
def __init__(self,path):
self.file = open(path,"rt",encoding="utf-8")
def read(self):
return self.file.read()
def __del__(self):
self.file.close()
tool = FileTool("a.txt")
print(tool.read())
__call__
# 执行时机: 在调用对象时自动执行(既对象加括号)
class A:
def __call__(self, *args, **kwargs):
print("call run")
print(args)
print(kwargs)
a = A()
a(1,a=100) # call run (1,) {'a': 100}
__slots__
"""
是一个类属性,用于优化对象内存占用
优化的原理: 固定属性数量,则这个类的对象无法再添加新的属性,解释器不会为这个对象创建名称空间,则没有__dict__
"""
import sys
class Person:
__slots__ = ["name"]
def __init__(self,name):
self.name = name
p = Person("jack")
print(sys.getsizeof(p)) # 对象占用的内存空间
p.age = 20 # AttributeError: 'Person' object has no attribute 'age'
print(p.__dict__) # AttributeError: 'Person' object has no attribute '__dict__'
. 语法实现原理
"""
getattr 用 对象.属性 访问属性而属性不存在时执行
setattr 用 对象.属性=值 设置属性时执行
delattr 用 del 对象.属性 删除属性时执行
getattribute 也是用来获取属性,如果属性存在则先执行getattribute再直接返回值,如果属性不存在则先执行getattribute再执行getattr再返回None
"""
class A:
def __getattr__(self,item):
print("__getattr__")
def __setattr__(self,key,value):
# super().__setattr__(key,value) # 等价于 self.__dict__[key] = value
print(key)
print(value)
self.__dict__[key] = value
print("__setattr__")
def __delattr__(self,item):
self.__dict__.pop(item)
print("__delattr__")
def __getattribute__(self,item):
print("__getattribute__")
# return self.__dict__[item] # RecursionError: maximum recursion depth exceeded while calling a Python object
return super().__getattribute__(item)
# 没有定义__getattribute__时
a = A()
a.name = 'jll' # __setattr__
print(a.name) # jll
a.__dict__['age'] = 18
print(a.age) # 18
del a.name # __delattr__
print(a.name) # __getattr__
# __getattribute__测试
b = A()
b.name = 'xxx'
print(b.name) # 当name存在时执行__getattribute__再返回值,当name不存在存在时先执行__getattribute__再执行__getattr__再返回None
[] 语法实现原理
"""
任何符号都会被解释器解释成特殊含义,例如: . [] ()
getitem 用中括号获取属性时执行
setitem 用中括号设置属性时执行
delitem 用中括号删除属性时执行
"""
class A:
def __getitem__(self,item):
print("__getitem__")
return self.__dict__[item]
def __setitem__(self,key,value):
print("__setitem__")
self.__dict__[key] = value
def __delitem__(self,key):
print("__delitem__")
del self.__dict__[key]
a = A()
a["name"] = "jack" # __setitem__
print(a["name"]) # __getitem__
del a["name"] # __delitem__
# 示例: 让对象既支持.取值又支持[]取值
class MyDict(dict):
def __getattr__(self,key):
return self.get(key) # 或者 return self[key]
def __setattr__(self,key,value):
self[key] = value
def __delattr__(self,key):
del self[key]
a = MyDict()
a["name"] = "jack"
print(a["name"])
print(a.__dict__) # {}
del a["name"]
a.age = 18
print(a.age)
print(a.__dict__) # {}
del a.age
运算符重载
# 当使用某个符号时,python解释器会为这个符号定义一个含义,同时调用对应的处理函数,当需要自定义对象的比较规则时,就可以在子类中覆盖大于、小于、等于等一系列方法
# 案例: 原本自定义对象无法使用大于小于等于来进行比较,可以自定义运算符来实现,让自定义对象也支持比较运算符
class Student():
def __init__(self,name,height):
self.name = name
self.height = height
def __gt__(self, other): # other指的是另一个参与比较的对象
print("__gt__")
return self.height > other.height
def __lt__(self, other): # 大于和小于只要实现一个即可,符号如果不同,解释器会自动交换两个对象的位置
print("__lt__")
return self.height < other.height
def __eq__(self, other):
print("__eq__")
if self.name == other.name and self.age == other.age:
return True
stu1 = Student("jack",180)
stu2 = Student("rose",160)
stu3 = Student("jack",180)
print(stu1 > stu2) # __gt__ True
print(stu1 < stu2) # __lt__ False
print(stu1 == stu3) # __eq__ True
迭代器方法
# 迭代器是指具有__iter__和__next__的对象,可以为对象增加这两个方法来让对象变成一个迭代器
# 实现自定义range
class MyRange:
def __init__(self,start,end,step):
self.start = start
self.end = end
self.step = step
def __iter__(self):
return self
def __next__(self):
a = self.start
self.start += self.step
if a < self.end:
return a
else:
raise StopIteration
for i in MyRange(1,10,2):
print(i) # 1 3 5 7 9
上下文管理
"""
在python中,上下文为是一段代码区间,例如: with open打开的文件仅在这个上下文中有效
上下文管理两个方法:
__enter__: 表示进入上下文,应该返回对象自己
__exit__: 表示退出上下文,可以有bool类型返回值,用于表示异常是否被处理,仅在上下文中出现异常有用
如果为True,表示异常已经被处理
如果为False,表示异常未被处理,程序将中断
当执行with语句时,会先执行__enter__,当代码执行完后再执行__exit__,或者代码异常会立即执行__exit__,并传入错误信息(exc_type(包含错误的类型).exc_val(错误的信息).exc_tb(错误的追踪信息))
"""
class MyOpen(object):
def __init__(self,path):
self.path = path
def __enter__(self):
self.file = open(self.path)
print("enter...")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("exit...")
self.file.close()
# return False
with MyOpen("a.txt") as m:
# "123"+1
print(m.file.read())
反射reflect
反射
"""
反射: 一个对象应该具备检测,增加,修改,删除自身属性的能力,就是通过方法操作对象属性(直接使用__dict__语法繁琐,其实这些方法就是对__dict__的操作进行了封装)
反射的四个内置函数:
hasattr(对象,"属性") 判断对象是否存在某个属性
getattr(对象,"属性") 获取对象属性,可以设置默认值: getattr(对象,"属性",默认值)
setattr(对象,"属性",值) 修改或添加对象属性
delattr(对象,"属性") 删除对象属性
"""
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
p = Person("jack",18)
print(hasattr(p,"name")) # True
print(getattr(p,"name")) # jack
print(getattr(p,"names",None)) # None
setattr(p,"name","jll")
print(p.name) # jll
setattr(p,"id","123")
print(p.id) # 123
delattr(p,"id")
反射使用案例:框架
框架代码
"""
框架设计方式:
当对象不是自己写的而是另一方提供的,就必须判断这个对象是否满足要求,是否是我需要的属性和方法
反射被称为框架的基石,因为框架的设计者,不能提前知道你的对象是怎么设计的,所以提供给框架的对象,必须通过检测之后才能使用,检测就是反射要做的事情
示例: 用于处理用户终端指令的小框架
如果框架代码中,写死了使用某个类,这是不合理的,因为无法提前知道框架使用者的类在什么地方类叫什么
所以应该为框架的使用者提供一个配置文件,要求对方将类的信息写入配置文件,然后框架自己去加载需要的模块
如此一来,框架就与实现代码彻底解耦了,只剩下配置文件
"""
import importlib
import settings
# 框架已经实现的部分
def run(plugin):
while True:
cmd = input("请输入指令:")
if cmd == "exit":
break
# 因为无法确定框架使用者是否传入正确的对象所以需要使用反射来检测
# 判断对象是否具备处理这个指令的方法
if hasattr(plugin,cmd):
# 取出对应方法
func = getattr(plugin,cmd)
func() # 执行方法处理指令
else:
print("该指令不受支持...")
print("see you la la!")
# 框架根据配置文件拿到需要的类的路径
path = settings.CLASS_PATH
# 从配置中单独拿出模块路径和类名称
module_path,class_name = path.rsplit(".",1)
# 根据模块的路径拿到模块
mk = importlib.import_module(module_path)
# 从模块中取出类
cls = getattr(mk,class_name)
# 实例化对象
obj = cls()
# 调用框架
run(obj)
插件部分(libs\plugins.py)
class WinCMD:
def cd(self):
print("wincmd 切换目录...")
def delete(self):
print("wincmd 删库跑路...")
def dir(self):
print("wincmd 列出所有文件...")
class LinuxCMD:
def cd(self):
print("Linuxcmd 切换目录...")
def rm(self):
print("Linuxcmd 删库跑路...")
def ls(self):
print("Linuxcmd 列出所有文件...")
配置文件
# 框架使用者在配置文件中指定配合框架的类是哪个
CLASS_PATH = "libs.plugins.LinuxCMD"
动态导入importlib模块
import importlib
# 根据模块的路径拿到模块
pl = importlib.import_module("libs.plugins") # 等价于 import libs.plugins
# 从模块中取出类
cls = getattr(pl,"WinCMD")
# 实例化产生对象
obj = cls()
obj.cd()
元类metaclass
元类
"""
元类: 用于创建类的类,万物皆对象,类也是对象,对象是通过类实例化产生的,类对象也是由另一个类实例化产生的,这个类就是元类,默认情况下所有类的元类都是type
"""
class Person(object):
pass
p = Person()
print(type(p)) # <class '__main__.Person'>
print(type(Person)) # <class 'type'> Person类是通过type类实例化产生的
# 直接调用type类来产生类对象
# 类的三个基本组成: 1.类的名字(字符串) 2.类的父类们(列表或元组) 3.类的名称空间(字典)
cls_obj = type("dog",(),{})
print(cls_obj) # <class '__main__.dog'>
自定义元类
"""
元类的作用:创建类对象时做一些限制时可以操作元类,即继承type类来编写自己的元类(只要继承了type就变成了元类),并覆盖__init__方法
"""
# 自定义一个元类
class MyType(type):
def __init__(self, clss_name, bases, namespace):
super().__init__(clss_name, bases, namespace)
print(clss_name, bases, namespace)
if not clss_name.istitle():
raise Exception("类名首字母需大写...")
# 为pig类指定元类为MyType,等价于 MyType("Pig",(),{})
class Pig(metaclass=MyType): # Pig () {'__module__': '__main__', '__qualname__': 'Pig'}
pass
class Pig(metaclass=MyType): # Exception: 类名首字母需大写...
pass
元类的__call__方法
"""
当调用类对象(类加括号)时会自动调用元类中的__call__方法,并将这个类作为第一个参数传入
类的元类覆盖byte类的__call__方法后,这个类就无法产生对象,元类必须调用super().__call__来完成对象的创建,并将其对象返回
作用:控制对象的创建过程,可以元类覆盖byte类的__call__方法
"""
# 示例1:把对象的所有属性变成大写
class MyMeta(type):
def __init__(self,name,bases,namespace):
super().__init__(name,bases,namespace)
print("init run")
def __call__(self, *args, **kwargs):
print("metaclass call run")
print(self)
print(args)
print(kwargs)
new_args = []
for a in args:
new_args.append(a.upper())
return super().__call__(*new_args,**kwargs)
class Dog(metaclass=MyMeta): # <==> MyMate("Dog",(),{}),此时会执行元类的__init__方法
def __init__(self,name,hobby):
self.name = name
self.hobby = hobby
def __call__(self, *args, **kwargs): # 不会执行类中的__call__方法,会执行元类中的__call__方法
print(self)
print("call run")
d = Dog("jack","study") # init run \ metaclass call run \ <class '__main__.Dog'> \ ('jack', 'study') \ {}
print(d) # <__main__.Dog object at 0x00000273BC857EE0>
print(d.name,d.hobby) # JACK STUDY
# 示例2:要求创建对象时必须以关键字参数形式来传参, (不能有位置参数,有就 抛异常)
class Mate(type):
def __call__(self, *args, **kwargs):
if args:
raise Exception("不允许使用位置参数!")
return super().__call__(*args,**kwargs)
# def __call__(self, *args, **kwargs):
# # 元类__call__方法所干的事如下:
# obj = object.__new__(self) # 创建一个空类对象
# self.__init__(obj,*args,**kwargs) # 让类对象去初始化
# return obj # 返回类对象
class A(metaclass=Mate):
def __init__(self,name):
self.name = name
a = A(name="jack")
元类的__new__方法
"""
当创建类对象时,会先执行元类中的__new__方法,拿到一个空对象,再调用__init__来对这个类进行初始化操作(__new__用于创建类对象并且自动调用__init__让对象去初始化)
注意:自定义元类覆盖了__new__方法,则__new__方法必须返回对应的类对象
总结: __new__方法和__init__都可以实现控制类的创建过程,__init__更简单
"""
class Meta(type):
def __new__(cls, *args, **kwargs):
print(cls) # <class '__main__.Meta'> 元类自己
print(args) # ('A', (), {'__module__': '__main__', '__qualname__': 'A'}) 创建类需要的参数: 类名,基类,名称空间
print(kwargs) # {} 空的
print("new run") # new run
return super().__new__(cls, *args, **kwargs)
def __init__(self,a,b,c):
super().__init__(a,b,c)
print("init run") # init run
class A(metaclass=Meta):
pass
单例设计模式
"""
单例:指的是一个类产生一个对象
为什么要使用单例:单例是为了节省资源,当一个类的所有对象的属性全部相同时,则没有必要创建多个对象
"""
# 方法1:
# 单例元类
class Single(type):
def __call__(self, *args, **kwargs):
if hasattr(self,"obj"): # 判断是否已存在对象
return getattr(self,"obj") # 有就返回
obj = super().__call__(*args,**kwargs) # 没有则创建
self.obj = obj # 并存入类中
print("new 了")
return obj
class Student(metaclass=Single):
def __init__(self,name):
self.name = name
# 只会创建一个对象
Student("jll1")
Student("jll2")
# 方法2:
class Mysql:
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = object.__new__(cls, *args, **kwargs)
return cls._instance
a = Mysql()
b = Mysql()
print(a) # <__main__.Mysql object at 0x0000022511613580>
print(b) # <__main__.Mysql object at 0x0000022511613580>
标签:__,name,self,面向对象,OOP,print,class,def
From: https://www.cnblogs.com/jlllog/p/17028152.html