面向对象
面向过程
面向过程就是先干什么后干什么,有顺序写,例如之前所写的ATM,就是面向过程。
优点:复杂问题简单化。缺点:扩展性比较差,其中一部分要修改,就要修改很多位置,牵一发而动全身。
对扩展性要求不高的地方可以使用面向过程。
面向对象
在生活中,对象就是“特征”和“技能”的结合体。
在过程中,对象就是属性(变量)和方法(函数和功能)的结合体。也就是功能和数据的结合体。
优点:扩展性强
缺点:复杂了
一般用于扩展性要求比较高的地方。
类的定义和对象的产生
实例化:掉用类的过程 :
1、产生一个空对象,2、会自动调用类中的__init__方法,将空对象已经调用类时括号中传入的参数一同传给__init__方法。3、返回初始化好的对象。
类是对象相似“数据”与“功能”的集合体。
类体中最常见的就是“变量”和“函数”的定义,但是类体中也可包含任意其他代码。函数写在类里就叫方法。
并且需要先定义类,再调用类产生对象。(先想功能中共有的)
类体代码在定义阶段就会执行,会产生类的名称空间,调用阶段不会触发,因为在定义阶段就已经开始运行。会把类的名称空间绑定给__dict__属性,如何查看:类名.__dict__。
类的数据属性是共享给所以对象用的,大家访问的地址一样,类改值可以直接改,但是对象改类的值无法改,当对象中没有要改的值时,会自己造一个实例化,调用类。调用类:类名()。
定制对象自己独有的属性
类有一个名为 __init__() 的特殊方法(构造方法),该方法在类实例化时会自动调用。init(初始化)调用类的时候会自动调用这个,定义阶段不会触发,调用阶段会。
调用类会自动触发类里的__init__方法,然后会把得到的对象本身当成第一个参数自动传递。
名字查找顺序
属性的查找顺序分两大类:
类属性:在类里面定义就是类属性
对象属性:对象自己独有的属性
class Student(): school = 'sh' def __init__(self,name,age): self.name = name self.age = age # print(Student.__dict__) stu = Student('lin','18') print(stu.__dict__) #{'name': 'lin', 'age': '18'} # #类查看 # print(Student.school) #sh # #类增加 # Student.salsry = 20000 # print(Student.__dict__) # #类修改 # Student.salsry = 28000 # print(Student.__dict__) # #类删除 # del Student.salsry # print(Student.__dict__) #对象查看 print(stu.age) #18 print(stu.name) #lin print(stu.school) #sh (这个比较特殊,当对象中没有时就会从类中找) #对象增加 stu.salary = 20000 print(stu.__dict__) #'name': 'lin', 'age': '18', 'salary': 20000} #对象修改 stu.salary = 28000 print(stu.__dict__) #{'name': 'lin', 'age': '18', 'salary': 28000} #对象删除 del stu.salary print(stu.__dict__) #{'name': 'lin', 'age': '18'}
练习题
# 定义两个英雄类: # 每个英雄初始化的数据有:昵称、生命值、法力、攻击力() # 定义一个计数器,记录产生了多少个对象 class Heros(): count = 0 def __init__(self, name, HP, power, attack): self.name = name self.HP = HP self.power = power self.attack = attack Heros.count += 1 hero = Heros('angle','10000','1000','2000') hero1 = Heros('di','20000','1000','5000') print(hero.__dict__) print(hero1.__dict__) print(hero.count)
绑定方法
关于self是对象自己
绑定给对象的方法:
函数第一个形参self是对象自己,绑定给对象的方法由对象调用,会把对象自己当成第一个参数传给方法的第一个形参。
绑定给对象的方法类也能调用,只不过目的不是让类来调用的,类来调用就是普通的方法,没有特殊之处,言外之意就是方法里面有几个参数就要传几个参数,包括self自己也要传。
class Student(): country = 'China' def __init__(self,name,age,gender): # (self是对象给自己) self.name = name self.age = age self.gender = gender def tell_info(self): self必须要,这样下面的参数才能传上来 print('%s的年龄是%s,性别是%s'%(self.name,self.age,self.gender)) stu = Student('lin','18','female') stu.tell_info() 会把stu传给自己当成第一个参数
绑定给类的方法:
需要用到一个装饰器,classmethod,会把类名自动当成第一个参数传给函数第一个形参。
import settings class Mysql(): def __init__(self,ip,port): self.ip = ip self.port = port @classmethod #该方法变成绑定给类的方法,而不是绑定给对象 def func(cls): #上面加入装饰器,这里就变成了cls print('111') return cls(settings.ip,settings.port) res = Mysql('123',111) # print(res.func()) Mysql.func() #绑定给类的方法就由类来调用,特殊之处就是,会把类名当成第一个参数传给方法的第一个形参,也就是()中不需要再传参了
非绑定方法
不绑定给类,也不绑定给对象的方法
uuid :只需要了解uuid4,但是只要长度一定,次数够多,就有重复的可能,但是概率很低。
电商里的订单号用uuid+时间戳截取得到。
import uuid res = uuid.uuid4() print(res) #e2679245-c2d5-415d-989c-089895574328
class Student(): country = 'China' def __init__(self,name,age,gender): # (self是对象给自己) self.name = name self.age = age self.gender = gender self.id = self.create() @staticmethod #静态方法,该方法谁都不绑定,类和对象都能直接调用,就是个普通方法 def create(): #()里就不需要参数了 import uuid return uuid.uuid4() stu = Student('lin','18','female') print(stu.id) #打印出随机数 re = str(stu.id) print(re[1:8]) #进行切分 print(re.replace('-','')) #去-
__class__ 既用到对象,又用到类,可以直接打印self.__class__.__name__,打印出类名。
隐藏属性
1、隐藏属性在类的定义阶段,发生了变形:__类名__属性名
2、不但可以隐藏类属性、方法、对象属性都可以隐藏
3、为了更好的对外做限制
class Student(): __country = 'China' #将country属性隐藏了, 变形后的数据目的不是让我们在外部使用的,但是在外部也能使用,只是没必要 #__Student__country:'China' def __init__(self,name,age,gender): # (self是对象给自己) self.__name = name self.__age = age self.__gender = gender def f2(self): print(Student.__country) stu = Student('lin','18','female') print(Student.__dict__) #正常的名称空间为'country': 'China' print(Student.__dict__) #如果将country隐藏了,则名称空间中就为'_Student__country': 'China', stu.f2() #内部给个接口,外部则可以访问到 #当init里的变量前都加入__,则结果 print(stu.__dict__) #{'_Student__name': 'lin', '_Student__age': '18', '_Student__gender': 'female'}
隐藏数据属性的意义,将属性隐藏后,需要内部开一个接口去访问隐藏属性,因为隐藏属性是开发者所编写的,所以可以在接口之上任意附加控制逻辑,控制使用者对属性的一些操作。
隐藏函数、方法属性的意义:不想外部使用,只想内部进行使用。隔离复杂度。
class Student(): __country = 'China' #将country属性隐藏了, 变形后的数据目的不是让我们在外部使用的,但是在外部也能使用,只是没必要 #__Student__country:'China' def __init__(self,name,age,gender): # (self是对象给自己) self.__name = name self.__age = age self.__gender = gender def f2(self): print(Student.__country) #修改名字 def set_name(self, varl): if type(varl) is not str: #可以加入任意修改的功能 return self.__name = varl stu = Student('lin','18','female') print(Student.__dict__) #正常的名称空间为'country': 'China' print(Student.__dict__) #如果将country隐藏了,则名称空间中就为'_Student__country': 'China', stu.f2() #内部给个接口,外部则可以访问到 #当init里的变量前都加入__,则结果 print(stu.__dict__) #{'_Student__name': 'lin', '_Student__age': '18', '_Student__gender': 'female'} #修改名字 stu.set_name('rui') print(stu.__dict__) #{'_Student__name': 'rui', '_Student__age': '18', '_Student__gender': 'female'}
property使用
是一个装饰器。用来绑定给对象的方法伪造成一个数据属性。
class BMI(): def __init__(self,weight,height): self.weight = weight self.height = height @property def bmi(self): return self.weight /(self.height **2) res = BMI(65,1.75) print(res.bmi) #当加入property后,调用时这里就不用加()了
class Student(): __country = 'China' def __init__(self,name,age): self.name = name self.age = age @property def country(self): return self.__country @country.setter def country(self,val): if type(val) is not str: return Student.__country = val @country.deleter def country(self): print('删除') del Student.__country stu = Student('lin',18) print(stu.country) stu.country = 'japan' print(stu.country) del Student.country
class Student(): __country = 'China' __city = 'shanghai' def __init__(self, name, age, gender): self.__name = name # _Student__name self.age = age self.gender = gender def get_country(self): return self.__country def set_country(self, v): if type(v) is not str: return Student.__country = v def del_country(self): print("可以删除了") del Student.__country """这种方式,是有顺序要求的""" country = property(get_country, set_country, del_country) stu = Student("kevin", 19, 'male') print(stu.country) # stu.country = 'Japan' # print(stu.a) # del stu.country
面向对象三大特征
封装:一种思想,把冗余的代码,重复的代码都封装成函数,以后每次调用都调用函数来用。
继承:
1、什么是继承?
继承就是新建类的一种方式,新建出来的类称为是子类或者叫派生类,被继承的类称为是父类或者叫基类
2、为什么要继承?
类解决什么问题:解决的是对象与对象之间的代码冗余问题
继承解决什么问题:解决的是类与类之间的代码冗余问题
3、怎么样继承?
新式类:继承了object类的子子孙孙类都是新式类
经典类:没有继承object类的子子孙孙类都是经典类
直接把类名写在括号里就是继承,括号里写的类是父类,括号外写的是子类
python支持多继承,括号里可以写多个类
查看继承子类.__bases__
print(Sub1.__bases__) print(Sub2.__bases__) print(Parent1.__bases__) # object
以学生老师为例
class People(): school = 'sh' def __init__(self,name,age): self.name = name self.age = age class Student(People): def __init__(self,name,age,course): People.__init__(self,name,age) self.course = course stu = Student('lin',18,'python') print(stu.school) #sh class Teacher(People): def __init__(self,name,age,level): People.__init__(self, name, age) self.level = level teacher = Teacher('rui',18,'h') print(teacher.name) #rui
对象属性的查找顺序:先从对象自己的名称空间查找,如果找不到,再去类中取查找,如果找不到,取继承的父类转中查找。
单继承下的属性查找
单继承:一个类只继承一个类
单继承下的属性查找顺序:先从对象自己的名称空间中查找,然后去产生对这个对象的类中查找,最后去继承的父类中查找。
class Foo(): def f1(self): print('foo.f1') def f2(self): print('foo.f2') class Bar(Foo): def f1(self): print('Bar.f1') obj = Bar() #实例化 obj.f2() #foo.f2 obj.f1() #Bar.f1 obj1 = Foo() obj1.f1() #foo.f1 obj1.f2() #foo.f2
隐藏后的属性查找
class Foo(): def __f1(self): #伪装成了_Foo__f1 print('foo.f1') def f2(self): print('foo.f2') self.__f1() class Bar(Foo): def __f1(self): #伪装成了_Bar__f1 print('Bar.f1') obj = Bar() obj.f2() #foo.f2 foo.f1 obj.f1() #报错,因为f1被隐藏了
多继承下的属性查找
多继承下的属性分为:菱形查找和非菱形查找
菱形查找分为:经典类和新式类
经典类:按照深度优先查询
新式类:按照广度优先查询 (在python3中都是新式类,所以多继承下的属性查找,如果属性找不到,就按照广度优先查询)
class A(): def text(self): print('a') class B(A): pass # def text(self): # print('B') class C(A): pass # def text(self): # print('C') class D(B,C): pass obj = D() obj.text() #此刻text输出的是“B”,如果将B中的text隐藏,则输出的是“C”,如果将C中的text隐藏,则输出“a”class A(): def text(self): print('a') class B(A): pass # def text(self): # print('B') class C(A): pass # def text(self): # print('C') class D(B,C): pass obj = D() obj.text() #此刻text输出的是“B”,如果将B中的text隐藏,则输出的是“C”,如果将C中的text隐藏,则输出“a”
super的使用
super的使用也就是说,当方法里有super时,就会按照mro的顺序往下走。
class A: def text(self): print('A') super().text() #因为这里有Text,有super还会按照mro继续往下走,所以输出有两个值 #当这里没有text时,则就会按照C中()里继承的顺序往下走,则输出则为B #当A这里有text但是函数里只有pass没有super时,就会什么都不输出,因为找到text就截至了 # pass class B: def text(self): print('B') class C(A,B): pass c = C() c.text() #A B print(C.mro()) #[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
多态和鸭子类型
多态性指的是可以在不用考虑对象具体类型的情况下而直接使用对象,这就需要在设计时,把对象的使用方法统一成一种:例如cat、dog、pig都是动物,但凡是动物肯定有talk方法,于是我们可以不用考虑它们三者的具体是什么类型的动物,而直接使用。
鸭子类型
class Dogs(): def eat(self): print('Dogs can eat') class Cats(): def eat(self): print('Cats can eat') class Monkeys(): def eat(self): print('Monkeys can eat') class cup(): def drink(self): print('it is cup ') def animals(obj): print('it should animals') return obj.eat() dog = Dogs() animals(dog) #dog里有eat所以animal可以直接调用 cup = cup() animals(cup) #因为cup不属于动物,它没有eat这个功能,所以无法调用函数animals
强制限制
import abc #这个类就变成抽象类,抽象类的特点:只能被继承,不能被实例化 #抽象类的方法都是抽象方法,而抽象方法不再实现具体的功能,而是用来限制子类的行为 class Animals(metaclass=abc.ABCMeta): @abc.abstractmethod def speak(self): print('animals') pass def eat(self): print('eat!') class Dogs(Animals): def speak(self): print('dogs') def eat(self): print('dogs eat') class Cats(Animals): def eat(self): print('Cats eat') dog = Dogs() dog.eat() #如果此处调用的方法父类里中没有加abc,也可以正常继承调用,因为speak父类里有abc,这里也有,所以可以调用 dog.speak() cat = Cats() cat.eat() #这里没有父类中有的abc,所以即使调用父类中无abc的,也不可以使用,会报错
组合
组合就是一个对象拥有一个属性,该属性的值是另一个对象。
class People(): def __init__(self,name,age,gender): self.name = name self.age = age self.gender = gender class Course(): def __init__(self,course_name,course_price): self.course_name = course_name self.course_price = course_price python = Course('python','1000') linux = Course('linux','2000') class Student(People): def __init__(self, name, age, gender, course=None): if course is None: course = [] super(Student,self).__init__(name,age,gender) self.course = course def choose_course(self): pass stu = Student('lin','18','female') print(stu.name) stu.course.append(python) print(stu.course)
class Foo(): def __init__(self,m): self.m = m #也就是相当于在这里加了一个 x = obj1 class Bar(): def __init__(self,n): self.n = n obj = Foo(10) obj1 = Bar(20) obj.x = obj1 print(obj.x.n) #20
魔法方法
类里面内置的双下划线的一些方法,他们有特殊功能,我们称之为魔术方法,简称为魔法。
__str__,__reper__方法
__str__:1、当打印或者输出对象时,会自动触发__str__执行。
2、返回值必须是字符串类型
3、内部返回什么结果,打印就是返回结果
__reper__:他的功能和__str__一样,只不过只写了他们两个其中之一都执行,如果两个都写,__str__优先级高于__reper__。
__str__举例:
class Student(): def __init__(self,name,age): self.name = name self.age = age def __str__(self): print('走了这') return '123' #返回值必须是字符串 stu = Student('lin',18) print(stu) #只有输出对象才会显示,此段代码输出结果是 走了这 123 print(stu.name) #lin
__reper__举例:(其和__str__一样)
class Student(): def __init__(self,name,age): self.name = name self.age = age def __repr__(self): print('走了这里吧') return '456' #返回值必须是字符串 stu = Student('lin',18) print(stu) #只有输出对象才会显示,此段代码输出结果是 走了这里吧 456 print(stu.name) #lin
当__str__和__reper__同时出现,优先显示__str__,其优先级更高
class Student(): def __init__(self,name,age): self.name = name self.age = age def __str__(self): print('走了这') return '123' #返回值必须是字符串 def __repr__(self): print('走了这里吧') return '456' #返回值必须是字符串 stu = Student('lin',18) print(stu) #只有输出对象才会显示,此段代码输出结果是 走了这 123 print(stu.name) #lin
__del__方法
1、当删除对象时,会自动触发函数执行
2、当程序结束时,也会自动触发执行
3、可以做一些垃圾清除操作
class Student(): def __init__(self,name,age): self.name = name self.age = age def __del__(self): print('我执行了') stu = Student('lin',123) print(stu.name) #输出结果:lin 我执行了 #会在程序执行完毕后触发__del__功能
isinstance(obj,cls)和issubclass(sub,super)
isinstance(obj,cls) 看对象是否是类里的 (obj是对象,cls是类)
issubclass(sub,super) 看类是不是子类
输出的结果都是布尔值
class People(): print('hi') #实例化时就会输出 class Animals(): print('nonono') class Student(People): def __init__(self,name,age): self.name = name self.age = age stu = Student('lin',123) #hi nonono print(stu.name) #lin print(isinstance(stu,Student)) #True print(issubclass(Student,People)) #True print(issubclass(Student,Animals)) #False
__doc__方法
打印注释,查看类内部的详细信息,这个特性不能继承父类
class People(): ''' 这里被注释啦 ''' print('hi') #实例化时就会输出 class Student(People): def __init__(self,name,age): self.name = name self.age = age stu =Student('lin',18) #hi print(People.__doc__) #这里被注释啦 print(Student.__doc__) #None __doc__不能继承父类
__enter__和__exit__方法
与with上下文管理器有关
with open ('a.txt') as f
__enter__:有with语句,就会触发__enter__,有返回值则给as变量,打印变量f就会输出返回值
class Student: def __init__(self,name): self.name = name def __enter__(self): print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量') return 123 def __exit__(self, exc_type, exc_val, exc_tb): print('with中代码块执行完毕时执行我啊') with Student('a.txt') as f: #有with则触发__enter__ print('执行代码') print(f) #这里输出结果为123,因为是__enter__中的返回值赋给了f #这边执行完毕后执行__exit__ #打印结果如下: # 出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量 # 执行代码 # 123 # with中代码块执行完毕时执行我啊
__exit__中有三个参数分别表示异常,当with语句有异常
__exit__(self, exc_type, exc_val, exc_tb)
exc_type:异常类型
exc_val:异常值
exc_tb:追溯信息
raise Exception 主动抛出异常
class Student: def __init__(self,name): self.name = name def __enter__(self): print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量') return 123 def __exit__(self, exc_type, exc_val, exc_tb): print('with中代码块执行完毕时执行我啊') print(exc_type) print(exc_val) print(exc_tb) with Student('a.txt') as f: #有with则触发__enter__ print('执行代码') raise AttributeError('这里这里') #执行结果如下: # 出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量 # 执行代码 # with中代码块执行完毕时执行我啊 # <class 'AttributeError'> # 这里这里 # <traceback object at 0x0000028AD1619DC8> # Traceback (most recent call last): # File "D:\pythonProject\class\class.py", line 722, in <module> # raise AttributeError('这里这里') # AttributeError: 这里这里
__setattr__,__delattr__,__getattr__方法
只有“.“语法才能触发
__setatter__:设置不存在的属性
__delattr__:删除
__getatter__:只有在使用点调用属性且属性不存在的时候才会触发
class Foo(): def __init__(self,y): self.y = y # def __getattr__(self, item): # print('属性不存在') #当属性不存在 # # def __delattr__(self, item): # print('删除') #删除属性 # # def __setattr__(self, key, value): # print('修改') #设置不存在属性 obj = Foo('10') #实例化中会执行出 修改 是因为初始化阶段这个值也是不存在的 obj.z = 1 #修改 obj.z #属性不存在 print(obj.z) #1 del obj.z #删除 print(obj.z) #找不到该值
关于修改,我们可以直接修改属性字典,来完成添加/修改属性的操作
obj.__dict__['x'] = 3 #正常赋值是无法修改的,只有这样才可以修改 print(obj.__dict__) #{'x': 3} print(obj.x) #3
__setitem__,__getitem,__delitem__
“[ ]“语法触发
class Foo: def __init__(self,name): self.name=name def __getitem__(self, item): print('1') print(self.__dict__[item]) def __setitem__(self, key, value): print('2') self.__dict__[key]=value def __delitem__(self, key): print('del obj[key]时,我执行') self.__dict__.pop(key) def __delattr__(self, item): print('del obj.key时,我执行') self.__dict__.pop(item) f1=Foo('sb') f1['age']=18 f1['age'] f1['age1']=19 del f1.age1 del f1['age'] f1['name']='alex' print(f1.__dict__)
__call__
class Foo(): def __init__(self): pass def __call__(self, *args, **kwargs): print('call') return 123 obj = Foo() print(obj()) #对象加()调用__call__
反射
通过字符串形式操作对象属性
getattr(stu,'name') stu里获取name属性,如果属性不存在会报错
getattr(stu,'age',666) age不存在,但是不会报错,会直接显示666
setattr()当属性存在就修改
delattr() 删除
hasattr()判断有没有返回布尔值
class Student(): school = 'sh' def __init__(self,name,age): self.name = name self.age = age def index(self): print('Student,index') stu = Student('lin',18) #getattr print(getattr(stu,'name')) #lin 也就是相当于stu.name一下都是 print(getattr(stu,'age1',666)) #没有age1,输出结果666 print(getattr(stu,'index')) #内存地址 #setattr 如果属性名存在就是修改,不存在就是增加 setattr(stu,'x',1) print(stu.__dict__) #{'name': 'lin', 'age': 18, 'x': 1} setattr(stu,'name','yee') print(stu.__dict__) #{'name': 'yee', 'age': 18, 'x': 1} #delattr delattr(stu,'age') print(stu.__dict__) #{'name': 'yee', 'x': 1} #hasattr print(hasattr(stu,'index')) #True
如何捕捉异常 try: 被检测的代码 except 异常类型: pass else: print() finally: print()
class Animal(): # @abc.abstractmethod def speak(self): raise Exception("请先实现speak方法") class People(Animal): def speak(self): #如果此处没有speak,就会报错 pass # pass """主动抛出异常""" stu = People() stu.speak()
自定义异常信息
class MyException(BaseException): def __init__(self, msg): self.msg = msg def __str__(self): return self.msg raise MyException("这是异常信息")
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能