1 面向过程:将一个复杂的问题,拆分成多个简单的小问题(流程化,按照固定的实现流程)从而简单化
优点:复杂的问题简单化
缺点:流程固定,牵一发动全身,扩展性差,维护性差
应用场景:对扩展性要求低的软件,比如系统内核,脚本程序,文本编辑器这些
2 面向对象:实际上在寻找一堆对象,让他们来完成任务
优点:扩展性高,维护性高,各对象耦合度低(一个对象有问题,不会对其他对象产生影响)
缺点:复杂程度比面向过程高,无法预知执行的结果(对于比如lol这样的游戏,不能把程序写死)
应用场景:需要较高扩展性的软件(比如直接与用户交互的程序, qq与微信)
当然对于一些不需要扩展的程序而言,使用面向对象反而增加了复杂度
二.类与对象
什么是类
是一个抽象概念,不是具体存在的,
类是通过提取一系列对象的相同特征和技能得到的
类的作用是用于标识对象与对象之间的差异,通过类就能大致了解一个对象的特征和行为
什么是对象
具备某些特征和技能的结合体,是具体存在的某个物体,(一切皆对象)
在生活中,先有对象再有类
再程序中,先有类再有对象
# 在类中描述对象的特征和行为
class Person:#类名首字母要大写
# 用变量来描述特征
name = "李四"
sex = "man"
age = 20
# 得到对象 通过调用类 ,也称之为实例化 或 创建对象
obj = Person()
print(obj)
#<__main__.Person object at 0x00000279452B7C50>
# 模块名为main 其包含一个Person类 通过Person类产生了一个对象 地址为0x00000279452B7C50
# 这是一个Person类的对象 其地址为0x00000279452B7C50
# 使用对象的属性(说的就是特征)
print(obj.name)
print(obj.age)
print(obj.sex)
# 通过__dict__可以获取一个对象中包含的内容
print(obj.__dict__)
{}
# 获取类中包含的内容
print(Person.__dict__)
{'__module__': '__main__', 'name': '李四', 'sex': 'man', 'age': 20, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
三.初始化函数
# 带有__开头__结尾的函数 是一些特殊内置函数,会在某个时间点自动触发执行
class Person:
# 初始化函数名称是固定 该函数会在调用类是时自动执行,self参数必须有,表示要进行初始化的对象,系统会自动传值
def __init__(self,name,age):
print("执行了 __init__")
print(self)
self.name = name
self.age =age
p1 = Person("张三丰",80)
print(p1.__dict__)
执行了 __init__
<__main__.Person object at 0x0000025BB20CD4A8>
{'name': '张三丰', 'age': 80}
p2 = Person("李狗蛋",20)
print(p2.__dict__)
执行了 __init__
<__main__.Person object at 0x0000025BB20CD2B0>
{'name': '李狗蛋', 'age': 20}
# init 函数用于初始化对象,它会在创建对象时,自动执行,并传入调用类时传递的参数,第一个参数表示要初始化的对象本身,
# self(第一个)参数不需要手动传递
# self表示对象自己 是一个形式参数,名字可以随便取,但是不建议修改
四.属性的访问顺序
"属性的访问顺序"
class Car:
c_type = "破鞋"
color = "red"
price = 400000
c1 = Car()
c2 = Car()
print(c1.__dict__) # {}
print(c2.__dict__) # {}
print(c1.c_type) # 破鞋
# 当对象中不存在是会到类中去找
c1.c_type = "法拉利"
print(c1.__dict__) # {'c_type': '法拉利'}
print(c2.__dict__) # {}
print(c1.c_type) # 法拉利
print(c2.c_type) #破鞋
# 如果对象中存在这个属性,优先问对象中的属性
print(Car.__dict__) # {'__module__': '__main__', 'c_type': '破鞋', 'color': 'red', 'price': 400000, '__dict__': <attribute '__dict__' of 'Car' objects>, '__weakref__': <attribute '__weakref__' of 'Car' objects>, '__doc__': None}
# 查找顺序为 对象 -> 类
# 当创建一个类的时候 会产生名称空间,存储类中名称和值的绑定关系
# 当创建一个对象的时候 会产生名称空间,存储对象中名称和值的绑定关系
# 类还有另一个作用 就是 作为对象的模板,所有属于同一个类的对象,都具备类中的公共内容
五.绑定方法
"""
绑定方法
什么是绑定 把两个东西捆绑在一起
什么是方法 方法 就是 函数
函数是专业术语,不好理解,面向对象编程思想,是要我们模仿现实生活中的抽象概念,为了方便理解就把函数称之为方法
绑定方法就是 把对象与函数进行绑定
为什么要把把对象与函数进行绑定
调用函数 就变成了调用对象的方法
对象本质上就是一种存放数据的容器
函数是用于处理数据的代码
绑定方法就是将数据与处理数据的函数绑定在一起
最终问题是 为什么要把数据与处理数据的函数绑定在一起?
如何使用绑定方法
"""
class Student:
school = "BeiJing"
def __init__(self,name,sex,age):
self.name = name
self.sex = sex
self.age = age
def learning(self):
print("正在学习..")
def sayHI(self):
print("hello my name is %s my age:%s my sex:%s" % (self.name,self.age,self.sex))
# 默认情况下 在类中定义的函数都是绑定方法,共同点是,都会讲对象作为第一个参数self
stu1 = Student("一个学生","man",18)
stu1.sayHI()
Student.sayHI(stu1)
# 当用用对象来调用类中的方法时,默认把对象传入方法中
# 而用类名来调用时,则需要手动传入对象
# print(stu1.sayHI)
#<bound method Student.sayHI of <__main__.Student object at 0x000001784F889C50>>
# 这是一个绑定方法,本质上是Student类中的sayHI函数 现在把这个函数绑定给了地址为0x000001784F889C50的对象
#
# stu2 = Student("李四","女",19)
# stu2.sayHI()
# print(stu2.sayHI)
# 只要拿到对象 就同时拿到了数据和处理数据的方法
class Student:
school = "beijing"
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
# 绑定方法分为两种 一种是绑定给对象的,一种绑定给类的
# 绑定给类的方法 使用一个装饰器叫classmethod,必须有一个参数,表示当前类,参数名也可以自己定义,建议不要修改
@classmethod
def print_school(cls): # 输出类里面叫school的属性
print(cls.school)
def print_school2(self): # 输出类里面叫school的属性
print(self.school)
# 这是绑定给对象的方法
def sayHello(self):
print(self.name, " 说: 你好")
# Student.print_school_name()
Student.print_school()
# 对象绑定方法 可以使用对象来调用 也可以使用类名来调用
# 在对象调用时会自动传入对象自己
# 类调用时不会自动传参
# 类的绑定方法,对象和类都能调用,并且都会自动传入这个类
Student.print_school()
stu1 = Student("印度阿三","woman",20)
stu1.print_school()
# 如何定义对象绑定方法 以及类的绑定方法
# 在调用时的区别
# 一个方法到底应该绑定给对象还是帮对给类?
# 当要处理的数据包含在类中时,就应该绑定给类
# 当要处理的数据包含在对象中时,就应该绑定给对象
# 有一个Dog类 每一个Dog对象都应该会叫 会跑 请用面向对象来完成
class Dog:
def __init__(self,nikename,gender,age):
self.nikename = nikename
self.gender = gender
self.age = age
def run(self):
print("不好了 ",self.nikename,"跑了 ")
def bark(self):
print("听",self.nikename,"在瞎叫...")
d1 = Dog("大金毛","母的",2)
d2 = Dog("大黄","公的",3)
d1.run()
d2.bark()
Dog.run(d1)
"""
# 类的绑定方法和对象的绑定方法的相同与不同
相同点:
1.都会自动传值
2.都可以被类和对象调用
不同点:
1.对象绑定方法再对象调用时 传的是对象自己 而类绑定方法字自动传的是类自己
2.第一个参数 个cls 一个叫self
为什么要绑定?
# 第一个问题传递参数,必须手动传递,很有可能传参顺序而发生错误
# 第二个问题每次处理数据 都需要手动传参数
# 第三个问题 当要处理的数据特别的多 就不能再定义为变量了 你可以使用列表出来存储要处理的数据
但是 每次处理 都需要先获取数据 在传递给处理数据的函数
之所以绑定 ,简化代码,提高效率
"""
非绑定方法
class Teacher:
def __init__(self,name,sex):
self.name = name
self.sex = sex
# @staticmethod 用于定义个非绑定方法
@staticmethod
def test_func(num):
print("test_func run!")
print(num)
Teacher.test_func(1)
t1 = Teacher("矮根","男")
t1.test_func(100)
print(t1.test_func)
# 什么是非绑定方法 再类中 即不绑定给类 也不绑定给对象
# 特点:没有自动传参数的效果 ,类和对象向都能调用,就是一个普通函数
# 当你的这个功能不需要访问类的数据 也不需要访问对象的数据,就可以作为一个非绑定方法
# 使用场景较少
练习
1.创建Student类
2.拥有以下属性: 姓名 性别 年龄 学校 班级
3.拥有以下方法
save(name) 其作用是将这个对象序列化到文件中
get_obj(name) 其作用是根据name从文件中反序列化为得到一个对象
import json
class Student:
school = "beijing"
def __init__(self,name,sex,age,classes):
self.name = name
self.age = age
self.sex = sex
self.classes = classes
def save(self):
dic = {"name":self.name,"sex":self.sex,
"age":self.age,"classes":self.classes}
with open(self.name,"wt",encoding="utf-8") as f:
json.dump(dic,f)
@classmethod
def get_obj(cls,name):
with open(name,"rt",encoding="utf-8") as f:
dic = json.load(f)
obj = cls(dic["name"],dic["sex"],dic["age"],dic["classes"])
return obj
# stu1 = Student("阿尔法","man",20,"py5期")
# stu2 = Student("张三","woman",20,"py5期")
# stu1.save()
# stu2.save()
stu = Student.get_obj("阿尔法")
print(stu)
print(stu.name)
六.面向对象中的常用方法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
""" """ # 判断某个对象是不是某个类的实例 # isinstance() class Person: pass class Student(Person): pass stu = Student() #判断 两个对象是不是同一个类型 print(type(1) == type(1)) # 判断stu对象是不是Student类的实例 print(isinstance(stu,Student)) # 是不是子类 # issubclass() # 判断一个类是不是 另一个类子类 (所有类都是object的子类或子子类) print(issubclass(Student,Person))
七.反射
其实就是反省,简单的说就是对象要具备一种修正错误的能力
hasattr 是否存在某个属性
getattr 获取某个属性的指
setattr 设置某个属性的指
delattr 删除某个属性
这个几个方法都有一个共同点: 都是通过字符串来操作属性,
class Student:
def __init__(self,name,sex,age):
self.name = name
self.age = age
self.sex = sex
def study(self):
print("学生正在学习...")
stu = Student("杨彬","woman",38)
# 当你获取到一个对象 但是并不清楚搞对象的内部细节时 就需要使用反射了
def test(obj):
if hasattr(obj,"name"):
print(getattr(obj,"name","没有name属性"))
test(stu) # 杨彬
setattr(stu,"school","beijing") # 为stu这个对象设置school为beijing
delattr(stu,"school") # 删除学校这个属性
print(getattr(stu,"school","没有学校属性")) # 这样打印出来就会出现没有学校属性
delattr(stu,"age") # 删除age这个属性
print(stu.age) # 由于被删除会报错 AttributeError: 'Student' object has no attribute 'age'
八.str
这个主要作用时自定义打印的内容,要注意的是,这个方法必须返回一个字符串 返回的是什么,就打印什么
class Test:
def __init__(self,name):
self.name = name
def __str__(self):
print("str run....")
return self.name
t = Test("安米")
print(t)
#str run....
#安米
# 在讲一个对象转换字符串时 本质就是在调用这个对象 __str__方法
print(str(t))
#str run....
#安米
九.del (析构函数)
将对象从内存中删除时会自动执行 但是注意 并不说用这个方法来删除对象 删除对象时解释器干的
该方法仅仅是 通知你的程序 这个对象即将被删除
在python中 有自动内存管理机制 所以 python自己创建的数据 不需要我们做任何操作
但是有一种情况 我们使用python打开了一个不属于python管理的数据
比如打开了一个文件 这个文件一定是操作系统在打开 会占用系统内存 而python解释器无法操作系统内存的
所以 当你的python解释器运行结束后 文件依然处于打开状态 这时候就需要使用del来关闭系统资源
简单地说 当程序运行结束时 需要做一些清理操作 就使用del
十.exec 方法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
""" exec execute的缩写 表示执行的意思 其作用 是帮你解析执行python代码 并且将得到的名称 存储到制定的名称空间 解释器内部也是调用它来执行代码的 """ # 参数一 需要一个字符串对象 表示需要被执行的python语句 # 参数二 是一个字典 表示全局名称空间 # 参数三 也是一个字典 表示局部名称空间 globalsdic = {} localsdic = {} exec(""" aaaaaaaaaaaaaaaaaaaa = 1 bbbbbbbbbbbbbbbbbbbbbbbbbbbb = 2 def func1(): print("我是func1") """,globalsdic,localsdic) # 如果同时制定了 全局和局部 则 会字符串中包含名称 解析后存到局部中 # print(globalsdic) print(localsdic) localsdic["func1"]() # # 如果只传了一个传参数 则 将字符串中包含名称 解析后存到全局中 # exec(""" # aaaaaaaaaaaaaaaaaaaa = 1 # bbbbbbbbbbbbbbbbbbbbbbbbbbbb = 2 # # """,localsdic)
十一.元类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
""" 一切皆对象 元类是指 用于产生类的类 type就是元类 所有的自定义类都是通过type实例化得来 """ #创建模块的过程 1.创建一个空的名称空间 2.执行内部的代码 3.将得到的名字放到名称空间中 # class也是一个对象 class Student(object): school = "北京大学!" def study(self): print("学习中...") # 使用type可以发现 类其实是type类型的实例(对象) print(type(Student)) # 我们可以自己调用type来实例化产生一个类 # myclass 包含的代码 code = """ name = "张三" age = 18 def hello(self): print("hello %s" % self.name) """ #类的名字 class_name = "MyClass" #类的的父类们 base_classes = (object,) #类的名称空间 namespace = {} exec(code,{},namespace) res = type(class_name,base_classes,namespace) print(Student) print(res.name) print(res.age) print(res.hello) # 1.类是由type实例化产生的 # 2.我们可以使用type来产生一个类 # 3.一个类是由 类名字 类的父类元祖 类的名称空间 三个部分组成 class Test(object): #Test = type("Test",(object,),{}) pass
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class MyMeta(type): # 用于新建类对象 def __new__(cls, *args, **kwargs): print("new run") # print(MyMeta) # print(*args) # 调用type通过的__new__方法来创建一个空的类对象 已经将三个组成部分都放到类对象中了 res = type.__new__(cls,*args) return res def __init__(self,class_name,bases,namespace): print("init run") print(self) class Student(metaclass=MyMeta): pass print(Student) """ new 与 init的区 __new__ 比__init__先执行 其作用是创建一个空的类对象 作为一个类对象 必须具备是三个组成部分 所以调用type中的__new__来完成组装 得到这个类对象后需要将其返回 以供__init__来使用 """
十二.通过__call__方法来控制对象的实例化过程
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
""" __call__ 调用的意思 在在对象被调用时 执行 函数 类 自定义元类 的目的 1.可以通过__call__ 来控制对象的创建过程 2.可用控制类的创建过程 """ # 自定义一个元类 元类也是一个类 但是需要继承type class MyMeta(type): # self 表示要创建对象的那个类(Person) *args是调用Person类时传入的参数 def __call__(self, *args, **kwargs): print("MyMte中的 call run'") print(self,*args,**kwargsl) # 下面的三步是固定写法 一个模板 只要你需要控制对象的创建过程 就应该先把模板写出来 # 1.创建空对象 obj = object.__new__(self) # 2.调用初始化方法 self.__init__(obj,*args,**kwargs) # self.__init__(obj) # 3.得到一个完整的对象 return obj # 修改Person类的元类为MyMeta class Person(metaclass=MyMeta): def __init__(self,name,age): self.name = name self.age = age def __call__(self, *args, **kwargs): print("call run...") #调用Person这个对象时 执行的是 Person的类(type)中__call__ 方法 p = Person("张三疯",80) print(p) # 当调用对象时 会执行该对象所属类中的__call__方法 # p() print(p.name) print(p.age) # class People: # def __init__(self,name): # self.name = name # pass # # # p = People() # p.anme = 1
十三.通过元类控制类的创建过程
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 要控制类的创建过程 只要找到类所属的类 中的__init__即可 class MyMeta(type): # self 刚建出来的类 # 第二个 类的名字 # 第三个 类的父类们 元组 # 第四个 这个类传进来的名称空间 def __init__(self,class_name,bases,namespace): print("============================") #print(self.__dict__) # 我要控制 类的名字 必须 是大写开头 if not class_name.istitle(): print("类名 必须大写开头...... ") # 该代码是主动抛出异常 raise TypeError("类名 必须大写开头...... ") #要空类的创建 必须包含__doc__这个属性 if not self.__doc__: raise TypeError("类中必须有文档注释.....") pass class Student(metaclass=MyMeta): # Student = MyMeta("Student",(object,),{}) """ 这是文档注释 可以通过__doc__来获取 这是一个学生类 """ # 在类的__init__中可以控制该类对象的创建过程 def __init__(self,name): print("-----------------------") print(self.__dict__) self.name = name print(Student.__doc__) # 元类使用总结: """ 元类是用于创建类的类 学习元类是为了 能控制类的创建过程 以及 类实例化对象的过程 一. 控制类的创建过程 1.创建一个元类 (需要继承type) 2.覆盖__init__方法 该方法 会将新建的类对象 类名 父类们 名称空间 都传进来 , 可以利用这些信息在做处理 3.对于需要被控制的类 需要指定metaclass 为上面的元类 二. 控制类实例化对象的过程 1.创建一个元类 (需要继承type) 2.覆盖__call__方法 会将 正在实例化对象的类 调用类是传入的参数 都传进来 3.在__call__方法中 必须要先编写模板代码 3.1创建空对象 3.2调用类的__init__方法来初始化这个空对象 3.3返回该对象 4.加入你需要控制的逻辑 类的三个组成部分 类名 父类们 名称空间 元类 -> 实例化产生 -> 类 -> 实例化产生 -> 对象 """
十四.单例模式
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
单例(一种设计模式(套路)) 是指一个类中只有一个对象 什么时候使用单例 当要处理的数据只有一份时 当所有对象的属性都相同时 1.通过classmethod 会有问题 依然可以通过调用类 产生新的对象 2.通过元类中的__call__方法 来实现 class MyMeta(type): obj = None def __call__(self, *args, **kwargs): if not MyMeta.obj: obj = object.__new__(self) self.__init__(obj,*args,**kwargs) MyMeta.obj = obj return MyMeta.obj #打印机类 class Printer(metaclass=MyMeta): """ 这是一个单例类 请不要直接实例化 使用get方法来获取实例 """ obj = None def __init__(self,name,brand,type): self.name = name self.brand = brand self.type = type def printing(self,text): print("正在打印 %s" % text) # 通过该方法来获取对象 可以保证只有一个对象 # 但是这还不够 因为 还是可以通过调用类产生新对象 # 就应该使用元类 来控制实例化的过程 __call__ # 在__call__ 中编写代码 保证每次调用call 都返回同一个实例 即可 @classmethod def get_printer(cls): if not cls.obj: obj = cls("ES005","爱普生","彩色打印机") cls.obj = obj print("创建了新的对象") return cls.obj # 以下三个对象 的数据完全相同 但是却 占用三分内存空间 # p1 = Printer("ES005","爱普生","彩色打印机") # p2 = Printer("ES005","爱普生","彩色打印机") # p3 = Printer("ES005","爱普生","彩色打印机") # 现在要处理问题就是 如何能够限制该类 只能实例化一个对象 p = Printer.get_printer() print(p) p = Printer.get_printer() print(p) p = Printer.get_printer() print(p) p = Printer.get_printer() print(p) p1 = Printer("ES005","爱普生","彩色打印机") p2 = Printer("ES005","爱普生","彩色打印机") print(p1) print(p2)