编程范式:实现一个任务有多种不同的编程方式,这些方式叫编程范式。
最重要的编程范式:面向过程 、 面向对象 和函数式编程。
面向过程:procedural programming uses a list of instruction to tell the computer what to do step by step.
优点:极大的降低了写程序的复杂度,只需要顺着要执行的步骤,堆叠代码即可。
缺点:这种编程方式维护起来难度越来越大,牵一发而动全身。
面向对象(object-oriented programming):简称oop编程,利用类和对象来创建各种模型来实现对真实世界的描述。
优点:解决了程序的扩展性,对某一个对象的修改会反映到整个体系中。面向对象的维护起来起来比较简单,基于面向对象写的程序可以使人容易理解你的代码逻辑。
缺点:可控性差,无法向面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题,即便是上帝也无法预测最终结果。
名词解释
类:具有相同属性和方法的一类事物
对象: 就是具体的拥有准确属性值的一类事物。
实例化:就是一个类创造一个的具体对象的过程。
实例化的过程:1构造对象。2初始化,3,返回一个初始化的对象。
类名的作用
类名有两种作用:
1属性引用。class.静态属性名就可以查看它的属性值。类名也可以调用自己类中的方法,但是必须实例化后才行:class.方法(实例化对象)。用类名可以删除属性
2.实例化对象:类名加上了括号就成为了一个对象。
静态属性:直接在类中定义的变量。静态属性是属于类的(直接和类名关联或者直接定义在class下。),
动态属性:在类中定义的方法或函数。
调用动态属性有两种方法:1 class.方法(obj)前提一定是先实例化了对象。2 obj.方法
对象属性:在类内和self关联,在类外和对象名关联的变量。对象给自己添加一个新属性,对别的对象没有任何影响。
__init__:初始化方法 是为了给某一个对象加上属性,这个方法不是必须有的。
self :代表实例化对象本身。当你用实例调用方法时,由解释器悄悄地传递给方法的。
对象可以使用类的,而类不可以使用对象的。
命名空间和类中各属性含义
静态属性属于类命名空间。动态属性属于类命名空间。类可以调用自己类命名空间的东西,但是不可以调用对象命名空间的东西。
对象属性属于对象命名空间,对象可以通过类指针调用静态属性(前提自己没有),可以通过类指针调用动态属性,可以调用对象属性。
class Foo: country = "china" # 静态属性 。类属性 country1 = ["中国"] def __init__(self, name,age):#初始化方法是为了给对象加上属性,它中的self是一个没有属性的对象,初始化完的对象,又传给alex这个对象了,相当于这步给Alex穿上了衣服。 不能叫做构造方法,因为构造方法是__new__. self.name = name self.age=age #这叫做对象属性 ,类不可以调用对象属性 def walk(self): # 动态属性,定义在类中的函数就叫做方法。这个self就是已经初始化完后的对象。 print("in the walk") alex = Foo("alex",25) # 实例化对象 egon = Foo("egon",36) alex.country1.append("日本") print(alex.country1) print(egon.country1) alex.country1 = [] # 这里进行了对Alex的赋值,只要是赋值,对象就会引用自己的属性,自己没有的时候才会引用类属性 print("alex.country1", alex.country1)
结果:
['中国', '日本'] #添加静态属性 ['中国', '日本'] alex.country1 [] #引用的自己的属性
结论:
类属性是共享属性,因为不管你实例化几次,类变量都只加载一次,实例化只是调用了__new__和__init__方法,并没有重新加载类变量
类名操作静态变量时,不管操作可变还是不可变的数据类型,静态变量会发生变化,
class Foo:
count=0
def __init__(self):
Foo.count=Foo.count+1 #用类名操作类属性来改变类属性的值
f=Foo()
f=Foo()
f=Foo()
print(f.count)
结果:
3
当对象操作静态变量时:
1.引用变量时:先在自己的命名空间里没有才会去类空间里找。
2.操作静态变量:
如果静态变量时一个可变数据类型,这时候是引用传递,当对其中的的元素进行操作时(这里的操作不是重新赋值,指的是增加,删除)那么全局都会生效
_count = [] def __init__(self): self._count.append(1) b = Base() print(b._count) print(Base._count) # 静态变量发生了改变 c = Base() print(c._count) print(Base._count) # 静态变量改变 #结果 [1] [1] [1, 1] [1, 1]
如果是对静态变量重新赋值或操作不可变数据类型,使用的是值传递,那么只是在对象自己的命名空间里增加了一个新属性这个新属性和静态属性的名字一样。相当于开了内存空间,不影响静态变量的值。
class Base: _count = 0 def __init__(self): self._new_count = self._count # 自己没有去拿静态变量 print(self.__dict__) self._count += 1 # 自己没有去拿静态变量,+1后复制给自己的_count属性 print(self.__dict__) b = Base() print(b._count) c = Base() print(c._count) print(Base._count) # 静态变量并没有改变 #结果 {'_new_count': 0} # {'_new_count': 0, '_count': 1} 1 {'_new_count': 0} {'_new_count': 0, '_count': 1} 1 0 #类静态变量并没有改变
类对象指针:
class Foo: def eat(self): print("吃猪食") Ton=Foo() print("Foo",Foo.eat) print("Ton",Ton.eat)
结果:
Foo <function Foo.eat at 0x0000019D7ACCFA60> Ton <bound method Foo.eat of <__main__.Foo object at 0x0000019D7ACCE940>>
为什么会这样,他们不是一样的额吗?Foo能直接调用eat,然而Ton必须通过类对象指针找到eat方法,注意:类不可以调用对象的属性,即,这个对象的属性只在自己的命名空间内。
__dict__
class.__dict__:返回的是一个字典。类可以通过这个方法查看类中所有的方法和属性,但是不能通过他来修改属性,只能查看,但是不能查看继承的方法和属性。
class Animal: home="北京" def __init__(self,name,aggr,life_value): self.name=name self.aggr=aggr self.life_value=life_value def eat(self): print("%s正在吃饭中"(self.name)) class Dog(Animal): """ 关于一个dog的类 """ phone=123 def __init__(self,name,kind,aggr,life_value): Animal.__init__(self,name,aggr,life_value) self.kind=kind def run(self): return (print("%s is eating" % (self.name))) dog=Dog("小黄","哈巴狗","20",100) print("类",Dog.__dict__)
结果:
类 {'__module__': '__main__', #所处模块,也就是判断是不是当前模块 'phone': 123, #静态属性 '__init__': <function Dog.__init__ at 0x0000015B35B2F510>, #init方法 'run': <function Dog.run at 0x0000015B35B2F598>, #方法 '__doc__': '\n 关于一个dog的类\n } #用于描述该类的作用,这个就是注释内的内容
obj.__dict__:返回一个字典,只可以查看对象中的所有属性包括继承父类的但是不能查看对象的方法和父类的方法。注意注意!!使用这个方法可以修改对象的属性值,增删改查都可以
class Animal: home="北京" def __init__(self,name,aggr,life_value): self.name=name self.aggr=aggr self.life_value=life_value def eat(self): print("%s正在吃饭中"(self.name)) class Dog(Animal): """ 关于一个dog的类 """ phone=123 def __init__(self,name,kind,aggr,life_value): Animal.__init__(self,name,aggr,life_value) self.kind=kind def run(self): return (print("%s is eating" % (self.name))) dog=Dog("小黄","哈巴狗","20",100) print("对象",dog.__dict__)
结果:
对象 {'name': '小黄', 'aggr': '20', 'life_value': 100, 'kind': '哈巴狗'}
dir()
dir()是Python提供的一个API函数,dir()函数会自动寻找一个对象和类的所有属性和方法(包括从父类中继承的属性),但是只有名称,没有对应的值
class Animal: home="北京" def __init__(self,name,aggr,life_value): self.name=name self.aggr=aggr self.life_value=life_value def eat(self): print("%s正在吃饭中"(self.name)) class Dog(Animal): """ 关于一个dog的类 """ phone=123 def __init__(self,name,kind,aggr,life_value): Animal.__init__(self,name,aggr,life_value) self.kind=kind def run(self): return (print("%s is eating" % (self.name))) dog=Dog("小黄","哈巴狗","20",100) # print("对象",dog.__dict__) print("对象",dir(dog)) print("类",dir(Dog))
结果:
对象 ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'aggr', 'eat', 'home', 'kind', 'life_value', 'name', 'phone', 'run'] 类 ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'eat', 'home', 'phone', 'run']
组合
组合:一个类的对象是另外一个类的对象的属性。其实组合表达的是:什么有什么的关系。比如你有生日,但是生日没有你,哈哈!!
作用:将一个类的对象拥有的属性,再将其定义成一个类,以提高代码的复用。刚看到这句话的时候,我很懵逼其实想想,也好理解,举个橙子,你有生日,它也有生日,这个时候就可以把生气定义成一个类了,这样以后再写有生日的类时,直接赋值(理解我说的赋值的概念)就行了,就不需要再在类中的init方法中写上Year,mon.day了,是不是提高了代码的利用率。
范例:要求一个类引用另一个类的属性。
方法一:
class Teacher: def __init__(self,name,age,sex): self.sex=sex self.name=name self.sex=sex class Birthday: def __init__(self,year,month,day): self.year=year self.month=month self.day=day birthday1=Birthday(1968,12,31) boss_gold=Teacher("太亮",40,"不详") boss_gold.birth=birthday1 #添加属性 print(boss_gold.birth.year) print(boss_gold.Birthday.year)
方法二(精简版,最终版)
class Teacher: def __init__(self,name,age,sex,year,month,day): self.sex=sex self.name=name self.sex=sex self.birth=Birthday(year,month,day)#Birthday实例化后变成了一个对象 class Birthday: def __init__(self,year,month,day): self.year=year self.month=month self.day=day boss_gold=Teacher("太亮",40,"不详",1968,12,31) print(boss_gold.birth.day)
结果:
31
组合例题二:求圆环的面积和周长
class Circle: def __init__(self,r): self.r=r def area(self): return 3.14*self.r**2 def perimeter(self): return 2*3.14*self.r class Ring: def __init__(self,outer_r,inner_r): self.outer_r=Circle(outer_r) self.inner_r=Circle(inner_r) def area(self): return self.outer_r.area()-self.inner_r.area() def perimeter(self): return self.outer_r.perimeter()-self.inner_r.perimeter() ring=Ring(20,10) print(ring.area()) print (ring.perimeter() #不要忘记调用的这个括号
类的三大属性
有用的博客:https://www.liujiangblog.com/course/python/44
继承
继承:把相同的代码放在父类中,子类的对象在子类中没有找到某一属性时,他就会去找父类的。是一种创建新类的方式。表达了谁是谁的关系。
继承机制实现了代码的复用,多个类公用的代码部分可以只在一个类中提供,而其他类只需要继承这个类即可实现父类的功能.
继承分为单继承和多继承。
多继承:一个类继承多个类,谁在前面先继承谁的,
父类:又称为基类,每个类的祖宗都是object
子类:又称为派生类
__bases__:查看类的父类用__bases__方法
class Animal: #这就是父类 def __init__(self,name,aggr,life_value): self.name=name self.aggr=aggr self.life_value=life_value class Dog(Animal): 这就是子类 def bite(self,people): people.life_value-=self.aggr huang=Dog("大黄",100,500) print(huang.name)
python 有两种类,经典类和新式类,python2有经典类(这样写class Dog)和新式类(这样写class dog(object):),python3中全是新式类(默认继承object,不需要加括号)这样写class Dog:。
抽象(是从具体到模糊)和继承(从模糊到具体),先抽象再继承。
补充类的继承
一.子类中没有__init__方法,子类调用父类中对象的属性和方法: 对象.属性值 对象.方法名()
class Animal: home="北京" def __init__(self,name,aggr,life_value): #这里的self指的就是dog对象 self.name=name self.aggr=aggr self.life_value=life_value def eat(self): print("%s正在吃饭中"(self.name)) class Dog(Animal): def run(self): return (print("%s is eating" % (self.name))) dog=Dog("小黄","20",100) print(dog.name)
结果:
小黄
二.子类中有__init__方法,子类调用父类中对象的属性和方法: 在自己的类中调用父类的__init__方法并传上父类的形中的形参, 父类.__init__(self,name,.....)
继承范例一:条件子类没有属性,从父类中调用
派生属性:子类中有的属性,而父类中没有的属性。
class Animal: def __init__(self,name,aggr,life_value): self.name=name self.aggr=aggr self.aggr=life_value def eat(self): print("%s正在吃饭中"%(self.name)) class Dog(Animal): def __init__(self,name,kind,aggr,life_value): Animal.__init__(self,name,aggr,life_value) #调用父类的属性,其实这里你不继承Animal也能运行,但是不能调用Animal中的方法,这里其实是得到了一个对象,
不知道你们理解不理解 self.kind=kind def eat(self): return (print("%s is eating"%(self.name))) dog=Dog("小黄","哈巴狗","20",100) print(dog.name)
结果:
小黄
范例一 条件:子类中有这个方法,但是我想调用父类的方法。父类名.方法名(子类中的对象)
class Animal: def __init__(self,name,aggr,life_value): self.name1=name self.aggr=aggr self.aggr=life_value def eat(self): print("%s正在吃饭中"%(self.name1)) class Dog(Animal): def __init__(self,name,kind,aggr,life_value): Animal.__init__(self,name,aggr,life_value) self.kind=kind self.name=name def eat(self): return (print("%s is eating"%(self.name))) dog=Dog("小黄","哈巴狗","20",100) Animal.eat(dog) #调用父类然后把子类的对象传入父类 print(dog.name1)#子类中有变量,要求选用父类的属性,直接调用父类的即可
结果:
小黄正在吃饭中
小黄
范例三,条件父类和子类都有方法,我想都调用出来。 #在自己类中的方法中调用父类的方法,这样父类的方法和子类的方法都会执行
class Animal: def __init__(self,name,aggr,life_value): self.name1=name self.aggr=aggr self.aggr=life_value def eat(self): print("%s正在吃饭中"%(self.name1)) class Dog(Animal): def __init__(self,name,kind,aggr,life_value): Animal.__init__(self,name,aggr,life_value) self.kind=kind self.name=name def eat(self): Animal.eat(self) #在自己类中的方法中调用父类的方法,这样父类的方法和子类的方法都会执行 return (print("%s is eating"%(self.name))) dog=Dog("小黄","哈巴狗","20",100) dog.eat()
结果:
小黄正在吃饭中 小黄 is eating
super()
super出现的背景:
当使用指名道姓的调用时,如果我们修改了父类的名字,那么所有使用的指名道姓的方法,都需要更改,这样更改很麻烦,所以这时候super就出现了.
super() 作用:按照mro顺序(方法解析顺序)调用父类的方法
mro():生成该类的继承顺序列表.对于你定义的每一个类,Python会计算出一个所谓的方法解析顺序(MRO)列表。 这个MRO列表就是一个简单的类继承顺序表。
注意!!!可以用super的方法来代替Animal.__init__(self,name,aggr,life_value)
例如:
class Animal: def __init__(self,name,aggr,life_value): self.name1=name self.aggr=aggr self.aggr=life_value def eat(self): print("%s正在吃饭中"%(self.name1)) class Dog(Animal): def __init__(self,name,kind,aggr,life_value): #Animal.__init__(self,name,aggr,life_value) super(Dog, self).__init__(name,aggr,life_value)#这是正规写法,注意后边的括号里不能穿self了 super().__init__(name,aggr,life_value)#这里是简写版 self.kind=kind self.name=name def eat(self): #Animal.eat(self) super(Dog, self).eat() #super里已经有参数了。eat里就不需要写 了 return (print("%s is eating"%(self.name))) dog=Dog("小黄","哈巴狗","20",100) dog.eat()
super方法和指名道姓方法的区别?
表面上看 super(FooChild, self).bar(message)方法和FooParent.bar(self, message)方法的结果是一致的,实际上这两种方法的内部处理机制大大不同,当涉及多继承情况时,就会表现出明显的差异来,直接给例子
class A: def __init__(self): print("Enter A") print("Leave A") class B(A): def __init__(self): print("Enter B") A.__init__(self) print("Leave B") class C(A): def __init__(self): print("Enter C") A.__init__(self) print("Leave C") class E(B, C): def __init__(self): print("Enter E") B.__init__(self) C.__init__(self) print("Leave E")
结果:
Enter E
Enter B
Enter A
Leave A
Leave B
Enter C
Enter A
Leave A
Leave C
Leave E
由此可以看出来:
基类A被执行了2次
我们使用super来看一下
class A: def __init__(self): print("Enter A") print("Leave A") class B(A): def __init__(self): print("Enter B") super(B, self).__init__() print("Leave B") class C(A): def __init__(self): print("Enter C") super(C, self).__init__() print("Leave C") class E(B, C): def __init__(self): print("Enter E") super(E, self).__init__() print("Leave E") E()
结果:
Enter E
Enter B
Enter C
Enter A
Leave A
Leave C
Leave B
Leave E
在super机制里可以保证公共父类仅被执行一次,至于执行的顺序,是按照MRO(Method Resolution Order):方法解析顺序 进行的。
super 调用紧跟着子类的父类但是在python2中用,这个方法弊端,如果我有n个父类,我想调用第一个父类如何调用,解决方法:用知名道姓的方法,但是在python3中可以这么用。另外super 在多继承中不只是寻找当前类的父类,而是根据mro()顺序。从A点出发从,一依据广度优先排序寻找父类,
注意:如果一个类继承两个类(A,B),我想调用B类中的方法和属性只能用知名道姓的方法不能用super,因为super只能调到离他最近的类。
多继承问题
在遍历图的时候,有两种常用的访问顶点的顺序.
一种是:深度优先DFS(depth first search):从根节点(没有被指向的节点)出发,走到最深处,然后回溯,在回溯的过程中如果有一条新的路径则走新的路径,然后再回溯,以此类推直到得到所有的顶点.
0->1->3->4->2->5->6
第二种是广度优先BFS(breadth first search):从根节点开始,按照距离根节点由近导员从左到右访问各个节点
0->1->2->3->4->5->6
砖石继承问题:其实就是多继承的继承顺序问题。
python2中的经典类:继承顺序为:B>>D>>C 使用了深度优先算法。记忆诀窍:博大精深.
python2.2中的新式类:继承循序为:B>>C>>D 使用了广度优先算法,在新式类中mor()函数可以查看类的继承关系。
在Python 2.3及其以后的版本中,新式类的MRO使用C3算法,我认为拓扑排序就是C3算法.
拓扑排序:
在一个有向无环图中:
- 选择一个入度为0的顶点(不被箭头所指)并输出
- 从图中删除此顶点以及所有它发出的边
- 从左到右继续找入读为0的顶点.
- 重复步骤1、2,直到所有点都被遍历
C3算法继承顺序为: E->C->A->D->B->object
深度优先的继承顺序为:E->C->D->A->B->object
多态
多态:一类事物有多种形态。如何来理解这句话呢,举个例子,print(3*3),会得到数字9,print(3*"*"),会得到***,这就是多态,print会自动根据数据类型来得出结果,你不用像其他语言那样定义数据类型。
多态的概念其实不难理解,它是指对不同类型的变量进行相同的操作,它会根据对象(或类)类型的不同而表现出不同的行为。
python天生自带多态,不崇尚继承的多态,崇尚鸭子类型的多态,比如len(),你不用提前告诉我他是谁只要他有长度属性,len()就可以计算出结果
多态其实就是鸭子类型 :一个动物如果走起路来像鸭子,叫起来像鸭子,那么他就是鸭子.
class Animal: def run(self): raise AttributeError('子类必须实现这个方法') class People(Animal): def run(self): print('人正在走') class Pig(Animal): def run(self): print('pig is walking') class Dog(Animal): def run(self): print('dog is running') peo1=People() pig1=Pig() d1=Dog() peo1.run() pig1.run() d1.run()
人、狗、猪都继承了动物类,并各自重写了run方法。可以看出不同的对象调用run方法,都能打印对应的信息。这就是多态。不同类型调用相同的方法呈现出不同的结果.同时这也是鸭子模型,只要有run方法就是动物.
以下的chatgpt的解释我认为很好
多态(Polymorphism)是面向对象编程(OOP)中的一个核心概念,它允许不同类的对象对相同的方法做出不同的响应。多态通过继承和接口实现,使得不同的类可以共享相同的接口,但具体的实现可以不同。 多态的关键点如下: 共享相同的接口:多态要求不同的类实现相同的方法或接口,这样它们就可以被视为属于同一个类别。这个接口可以是抽象类的方法、接口的方法或普通类的方法。 不同的实现:虽然多个类共享相同的接口,但它们可以有不同的方法实现。这意味着当调用相同的方法时,具体的操作取决于对象的类型。 运行时绑定:多态实现了运行时绑定,即在运行时确定要调用的方法,而不是在编译时。这允许程序在运行时适应不同的对象类型。 多态的主要优点和应用包括: 代码重用:通过多态,可以定义通用的方法和接口,以便多个类可以共享相同的代码。这提高了代码的重用性和可维护性。 灵活性:多态增加了代码的灵活性,允许程序在运行时动态适应不同的对象类型。这对于编写通用的、可扩展的代码非常有用。 抽象性:多态可以帮助隐藏具体的实现细节,使得代码更加抽象和高层次,同时提供了一种规范和协议。 class Animal: def speak(self): pass class Dog(Animal): def speak(self): return "Woof!" class Cat(Animal): def speak(self): return "Meow!" def animal_sound(animal): return animal.speak() dog = Dog() cat = Cat() print(animal_sound(dog)) # 输出: Woof! print(animal_sound(cat)) # 输出: Meow! 在这个示例中,animal_sound 函数接受一个 Animal 类的对象,并调用 speak 方法。尽管它不知道具体的对象类型是 Dog 还是 Cat,但它可以正确地调用相应的 speak 方法,实现了多态。
Python中哪些地方实现了多态?
Python作为一种面向对象的编程语言,广泛地使用了多态的概念,它体现在不同方面的编程实践中。以下是一些Python中使用多态的常见情况:
1. 方法重写:Python支持方法重写,子类可以重写父类的方法,从而实现多态。当通过子类对象调用方法时,实际调用的是子类的方法,而不是父类的方法。
class Animal: def speak(self): pass class Dog(Animal): def speak(self): return "Woof!" class Cat(Animal): def speak(self): return "Meow!" dog = Dog() cat = Cat() print(dog.speak()) # 输出: Woof! print(cat.speak()) # 输出: Meow!
2.抽象基类:Python的abc模块允许定义抽象基类,其中包含抽象方法,子类必须实现这些方法。这实现了多态,因为不同的子类可以实现相同的抽象方法。
from abc import ABC, abstractmethod class Shape(ABC): @abstractmethod def area(self): pass class Circle(Shape): def __init__(self, radius): self.radius = radius def area(self): return 3.14 * self.radius * self.radius class Square(Shape): def __init__(self, side_length): self.side_length = side_length def area(self): return self.side_length * self.side_length
3. 参数多态性:Python函数和方法的参数可以接受不同类型的对象,只要这些对象实现了所需的接口。这允许函数和方法在不同类型的对象上执行相同的操作。
def calculate_area(shape): return shape.area() circle = Circle(5) square = Square(4) print(calculate_area(circle)) # 输出: 78.5 print(calculate_area(square)) # 输出: 16
4.鸭子类型:Python使用鸭子类型,即如果一个对象走起路来像鸭子、叫起来像鸭子,那它就可以被视为鸭子。这意味着 Python 不关心对象的具体类型,只要它具有所需的方法和属性,就可以进行操作。
class Duck: def speak(self): return "Quack!" class Robot: def speak(self): return "Beep Boop!" def get_sound(entity): return entity.speak() donald = Duck() rob = Robot() print(get_sound(donald)) # 输出: Quack! print(get_sound(rob)) # 输出: Beep Boop!
封装
封装是面向对象编程(OOP)中的一个重要概念,它指的是将数据(属性)和操作(方法)封装在类内部,同时隐藏了数据的具体实现细节,使其不直接暴露给外部。
封装:封装分为2种情况:广义上的封装和狭义上的封装.
1.广义上指的是:把一些属性和方法封装到类中,用类名来调用实现
2.狭义上的封装。隐藏对象的属性和实现的细节,只能在类内进行调用,比如__属性名私有静态属性和私有方法
# 看到一篇好的文章的解释 封装是指将数据与具体操作的实现代码放在某个对象内部,使这些代码的实现细节不被外界发现,外界只能通过接口使用该对象,而不能通过任何形式修改对象内部实现,
正是由于封装机制,程序在使用某一对象时不需要关心该对象的数据结构细节及实现操作的方法。使用封装能隐藏对象实现细节,使代码更易维护,同时因为不能直接调用、修改对象内部的私有信息
,在一定程度上保证了系统安全性。类通过将函数和变量封装在内部,实现了比函数更高一级的封装。
【好处】
1. 将变化隔离;
2. 便于使用;
3. 提高复用性;
4. 提高安全性;
[封装原则】
1. 将不需要对外提供的内容都隐藏起来;
2. 把属性和方法都隐藏,提供公共方法对其访问。
封装实例:
在rest-framework中,我们要经常使用状态码比如这样
ret = {"code": 1000, "data": None, "error": None}
我们就可以把ret封装成一个对象,以后取值和赋值,都用对象的方法来获取.
class BaseResponse: def __init__(self,code=1000,data=None,error=None):#提供默认参数,这招高明 self.code=code self.data=data self.error=error class DegreeCourseviewset(GenericViewSet): renderer_classes = [JSONRenderer] # renderer_classes = [BrowsableAPIRenderer] def list(self, request, *args, **kwargs): """ 学位所有课程的查看 :param request: :param args: :param kwargs :return: """ ret=BaseResponse() try: degreecourse_list = DegreeCourse.objects.all().values('id','name').order_by("id") # only得到的是queryset的列表对象 temp = DegreeCourseserliazer(degreecourse_list, many=True) #把分页放进序列化中 ret.data = temp.data except Exception as e: ret.code = 1001 ret.error = "数据获取失败" return Response(ret.__dict__)#利用面向对象中的__dict__的方法,把初始化的属性变成一个字典的形式
私有静态属性范例:
class Dog: __role="dog" #变成可私有静态属性 def func(self):` print(self.__role) #可以这样调用私有静态属性,理解为直接变成_类名__名字,Dog._Dog__role dog=Dog() dog.func()
注意:从类的外面不能直接调用,在类中加上了一层密码,其实也能在类外面调用,但是不推荐这样调用。
私有方法范例:
class Dog: __role="dog" #变成可私有静态属性 def __discount(self):#变成私有动态属性 print("in__func") def price(self): self.__discount() #这样就可以调用私有方法 dog=Dog() dog.price()
封装实例:
class Room: def __init__(self,name,price,length,width): self.name=name self.price=price self.__length=length #以这种方法来隐藏属性 self.__width=width def area(self): return self.__length**self.__width house=Room("小超超",10000,2,1) print(house.area())
注意:私有属性不能被继承。
property
@property 属性方法:把一个方法伪装成一个属性,伪装后用调用属性的方法来调用它。注意,属性的值是这个方法的返回值,这个方法不能有参数。
class Person: def __init__(self,name,height,weight,): self.name=name self.heigh=height self.weight=weight @property def bmi(self): return self.weight/(self.heigh**2) p=Person("李岩",1.7,58) print(p.bmi)#直接把他当做变量调用
@伪装的方法名.setter ,我们知道隐藏属性,用对象是没办法修改的,setter装饰器就是用来修改隐藏属性的,这个在项目中遇到了
class Foo: __password = "123" @property def password(self): return self.__password @password.setter def password(self, raw): """ 当通过obj.password=1233会调用该方法 :param raw: :return: """ self.__password = generate_password_hash(raw) if __name__ == '__main__': f = Foo() f.password = '1234' print(f.password)
结果:
pbkdf2:sha256:150000$y44tJnfm$27001896267c7791d8d7f54051162b814c77cb4256da1c6d3ca360c205272636
私有属性+property 1让对象的值变得更安全了。2.获取到的对象的值可以进行加工,3修改对象的值可以进行验证。
property()
class C(object): def __init__(self): pass def getx(self): return self._x def setx(self, value): self._x = value def delx(self): del self._x x = property(getx, setx, delx, "I'm the 'x' property.") c=C() c.x=66 print(c.x)
当你c.x的时候它会调用property中的getx方法得到返回值,当你c.x=66它会调用setx方法给x设置属性值,当del c.x的时候会调用delx方法删除x的属性值
在类中可以定义3种方法:
1.普通方法 :就是我们用得最多的方法,有self的方法
2.类方法 :@classmethod
3.静态方法 :@staticmethod
@classmethod 类方法 :好处不需要实例化节省了内存开销,直接可以用类名调用。使用范围,需要使用静态变量,不需要使用对象操作的时候。@classmethod也不需要self参数,但第一个参数需要是表示自身类的cls参数。
范例要求,我们修改私有属性,通过类方法,然后查看修改后的私有属性
class Goods: __discount=0.8 @classmethod def change_discount(cls,new_discount): cls:代表当前方法所代表的类 cls.__discount=new_discount @classmethod def get_discount(cls): return cls.__discount Goods.change_discount(0.9) print(Goods.get_discount())#这里不需要传对象
结果:
0.9
@staticmethod 静态方法:如果这个方法既不需要使用静态变量,也不需要使用对象相关的操作,就使用静态方法。
- @staticmethod不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样。
静态方法和类方法的区别?
1.首先是写法上的不同:类方法需要传入类变量cls,静态方法不需要
静态方法不可以使用类变量和实例变量(init)的,类方法能够访问类变量,但是不能访问实例变量
为什么会有这个方法呢?
我们知道面向对象中的函数必须写在类中,但是有些函数和类对象没有关系,为了方便这些函数,就出现了@staticmethod
class A: @staticmethod def func(): #不需要传任何参数 print(123) A.func()
结果:
123
强调,注意注意注意:静态方法和类方法虽然是给类准备的,但是如果实例去用,也是可以用的
设计模式
抽象类和接口类是一种设计模式
接口类:是一个规范。规范子类的一个模板,只要在接口中定义,子类就必须实现其规范的方法。如果子类中的方法错了或者没有这个方法,就会直接继承父类中的该方法,可以在父类中定义这个方法,但是不实现其功能,而是实现其能反映出子类中没有该方法,其实就是提醒子类中该方法没有实现,这就是为什么说接口类不实现,如果要是实现了,怎么能知道子类中不实现此方法。这个过程可以用一个模块所代替就是ABC模块,接口类不能被实例化,鼓励多继承。多继承,父类不实现。
接口只是定义了一些方法,而没有去实现,多用于程序设计时,只是设计需要有什么样的功能,但是并没有实现任何功能,这些功能需要被另一个类(B)继承后,由 类B去实现其中的某个功能或全部功能。
from abc import ABCMeta,abstractmethod
class payment(metaclass=ABCMeta): #接口类,是规范子类的模板,
@abstractmethod #装饰器 自动检查子类中的方法名。它就替代了pass中要反映子类中没有实现该方法的条件
def pay(self):pass #这是不实现
class Phone_pay(payment):
def pay(self,money):
print("正在使用苹果支付%s元"%money)
class Alibab(payment):
def pay(self,money):
print("正在使用支付宝支付%s元"%money)
class Wet_pay(payment):
def fuqian(self,money):
print("正在使用微信支付%s元" % money)
def pay(obj,money):
return obj.pay(money)
apple=Phone_pay()
zhifubao=Alibab()
wetchat=Wet_pay()
pay(wetchat,200)
抽象类:抽象类也是一个规范。抽象类可以实现子类中共有的属性和方法。而接口类是不实现的,这个你忘记了没。与java一样,python也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承但不鼓励多继承,不能被实例化。(不太重要,以后很长时间都会用不到)
为什么要有抽象类?
如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。这个父类有什么区别呢,抽象类不能被实例化,抽象类规范子类中必须有抽象类中的定义的方法,而对于父类来说,子类中不需要有父类的方法,需要时直接继承就行了。这里最容易和父类混淆了。
比如我们有香蕉的类,有苹果的类,有桃子的类,从这些类抽取相同的内容就是水果这个抽象的类,你吃水果时,要么是吃一个具体的香蕉,要么是吃一个具体的桃子。。。。。。你永远无法吃到一个叫做水果的东西。
从设计角度去看,如果类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的。
从实现角度来看,抽象类与普通类的不同之处在于:抽象类中有抽象方法,该类不能被实例化,只能被继承但不鼓励多继承,且子类必须实现抽象方法。这一点与接口有点类似,但其实是不同的,即将揭晓答案。