面向过程:
核心是“过程”二字
过程的终极奥义就是将程序流程化
过程就是“流水线”,分步骤来解决问题
面向对象:
核心是“对象”二字
对象的终极奥义就是将程序整合 对象是“容器”,用来盛放数据与功能的程序=数据+功能
一种数据和功能整合起来就是面向对象思想编程列表也可看做对象
什么是类?
类也是容器,该容器用来存放 同类对象 共有的数据与功能
# 在程序中先定义类,在调用类产生对象 # 一、先定义类 # 类式对象相似数据与功能的集合体 # 所以,类体中常见的代码是变量和函数的定义,但是类体其实是可以包含任意其他代码的 # 注意:类体代码是在类定义阶段就会立即执行,会产生类的名称空间 # class Student: # # 1.变量的定义 # stu_school='oldboy' # # # 2.功能的定义 # def tell_stu_info(stu_obj): # print('学生信息:名字%s 年龄:%s 性别: %s' %( # stu_obj['stu_name'], # stu_obj['stu_age'], # stu_obj['stu_gender'] # )) # print('=====>') # 属性访问的语法 # print(Student.__dict__) # 查看内存空间里面的东西 # print(Student.__dict__['stu_school']) # 访问数据属性 # print(Student.stu_school) # 与上面的功能一样,但是写起来要简单些 # 访问函数属性 # print(Student.tell_stu_info) # 二、调用类产生对象 # stu1=Student() # stu2=Student() # 代码太多 # stu1.stu_name='andy' # stu1.stu_age=12 # print(stu1.__dict__) # # stu2.stu_name='lili' # stu2.stu_age=18 # print(stu2.__dict__) # 解决方法1: # def init(obj,x,y,z): # obj.stu_name=x # obj.stu_age=y # obj.stu_gender=z # init(stu1,'andy',18,'男') # init(stu2,'lili',12,'女') # print(stu1.__dict__) # print(stu2.__dict__) # 解决方法2: class Student: # 1.变量的定义 stu_school='oldboy' def __init__(obj, x, y, z): obj.stu_name = x obj.stu_age = y obj.stu_gender = z # 2.功能的定义 def tell_stu_info(stu_obj): print('学生信息:名字%s 年龄:%s 性别: %s' %( stu_obj['stu_name'], stu_obj['stu_age'], stu_obj['stu_gender'] )) # 调用类的过程又称为实例化 # 过程: # 1、先产生一个空对象 # 2、然后自动调用__init__方法,将空对象已经调用类时括号内传入的参数一同传入给__init__方法 # 3、返回初始化完的对象 stu1=Student('andy',12,'男') stu2=Student('lili',18,'女') print(stu1.__dict__) print(stu2.__dict__) # 总结init方法 # 1、会在调用类时自动触发执行,用来为对象初始化自己独有的数据 # 2、__init__ 内应该存放为对象初始化属性的功能,但是可以存放任意其他代码,想要在类调用时就立刻执行的代码都可以放到该方法内 # 3、__init__方法必须返回None,不用写return None,默认就是返回None,没有返回值
属性查找
类有两种属性:
1、类的数据属性
类的数据属性是所有对象共享的
2、类的函数属性
类的函数属性是绑定给对象用的
class Student: # 1.变量的定义 stu_school='oldboy' def __init__(obj, x, y, z): obj.stu_name = x obj.stu_age = y obj.stu_gender = z # 2.功能的定义 def tell_stu_info(stu_obj): print('学生信息:名字%s 年龄:%s 性别:%s' %( stu_obj.stu_name, stu_obj.stu_age, stu_obj.stu_gender )) def choose(self,x): print('正在选课') self.course=x # 类总存放的是对象共有的数据与功能 # 类可以访问到数据属性和函数属性 # 类中的数据是共享给所有的对象用的,访问的地址是一样的 stu1=Student('andy',12,'男') stu2=Student('lili',18,'女') # print(stu1.__dict__) # print(stu1.stu_age) # print(Student.stu_school) # stu1.stu_school='xxx' # print(stu1.stu_school) # 类中定义的函数主要是给对象使用的,而且是绑定给对象的,虽然所有对象指向的都是相同的功能, # 但是绑定到不同的对象就是不同的绑定方法,内存地址各不相同 # 类调用自己的函数属性必须严格按照函数的用法来 # Student.tell_stu_info(stu1) # Student.tell_stu_info(stu2) # 绑定方法的特殊之处在于:谁来调用绑定方法就会将谁当做第一个参数自动传入,不用传入参数 # print(Student.tell_stu_info) # print(stu1.tell_stu_info) # 在类中新增一个函数,要传入一个参数 规范使用self # stu1.tell_stu_info() # stu2.tell_stu_info() # 第一个参数不用传 stu1.choose('Python全站开发') print(stu1.course) # l=[1,2] # print(type(l)) # <class 'list'>
面向对象编程有三大特性:封装、继承、多态,其中最重要的一个特性就是封装。
1、什么是封装
封装是面向对象三大特性最核心的思想
封装指的就是把数据与功能都整合到一起
封装就是整合
2、将封装的属性进行隐藏操作
如何隐藏:双下划线开头
class Foo: __x=1 # '_Foo__x' def __f1(self): # '_Foo__f1' print('from test') def f2(self): print(self.__x) print(self.__f1) print(Foo.__dict__) print(Foo._Foo__x) # 可以访问 注意1 这种变形只在类定义里面发生 obj = Foo() obj.f2() # 注意2
注意:
1、在类外部无法直接访问双下滑线开头的属性,但知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,所以说这种操作并没有严格意义上地限制外部访问,仅仅只是一种语法意义上的变形。
2、对内不对外, 在类内部是可以直接访问双下滑线开头的属性的 。
3、 变形操作只在类定义阶段发生一次,在类定义之后的赋值操作,不会变形。
为何要隐藏属性?
1、隐藏数据属性
将数据隐藏起来就限制了类外部对数据的直接操作,然后类内应该提供相应的接口来允许类外部间接地操作数据,接口之上可以附加额外的逻辑来对数据的操作进行严格地控制
class People: def __init__(self,name): self.__name = name def get_name(self): # 别人想用可以间接的访问这个接口来访问 print(self.__name) def set_name(self,val): # 也可以通过接口去修改内容 self.__name=val obj = People('upup') obj.get_name() obj.set_name('UPUP') obj.get_name()
2、隐藏函数属性
隐藏函数的目的是隔离复杂度, 例如ATM程序的取款功能,该功能有很多其他功能组成,比如插卡、身份认证、输入金额、打印小票、取钱等,而对使用者来说,只需要开发取款这个功能接口即可,其余功能我们都可以隐藏起来
class ATM: def __card(self): #插卡 print('插卡') def __auth(self): #身份认证 print('用户认证') def __input(self): #输入金额 print('输入取款金额') def __print_bill(self): #打印小票 print('打印账单') def __take_money(self): #取钱 print('取款') def withdraw(self): #取款功能 self.__card() self.__auth() self.__input() self.__print_bill() self.__take_money() obj=ATM() obj.withdraw()
property装饰器
property,可以将类中的函数“伪装成”对象的数据属性
class People: def __init__(self,name,weight,height): self.name = name self.weight = weight self.height = height # # 定义函数的原因 # 通过计算得到的,应该是触发功能计算的 # bmi是随着身高和体重的数据而变化的,不是一个固定的值 # 但是,bmi更像是一个数据属性,而不是功能 # 这个时候就可以加一个property属性,绑定给对象的一个方法伪装成数据属性 @property def bmi(self): return self.weight / (self.height ** 2) obj1 = People('upup',60,1.67) # print(obj1.bmi()) print(obj1.bmi)
property其他知识
当隐藏数据后,需要开接口供别人使用,可以使用以下方式
class People: def __init__(self,name): self.__name = name def get_name(self): return self.__name def set_name(self,val): if type(val) is not str: print('必须传入str类型') return self.__name = val def del_name(self): print('不能删除') name=property(get_name,set_name,del_name) obj1 = People('upup') # print(obj1.get_name()) # obj1.get_name() # obj1.del_name() print(obj1.name) obj1.name='up' #修改数据 print(obj1.name)
修改后:
class People: def __init__(self,name): self.__name = name @property def name(self): # get_name 改为name return self.__name @name.setter def name(self,val): # set_name 改为name if type(val) is not str: print('必须传入str类型') return self.__name = val @name.deleter def name(self): print('不能删除') name=property(get_name,set_name,del_name) obj1 = People('upup') # print(obj1.get_name()) # obj1.get_name() # obj1.del_name() print(obj1.name) obj1.name='up' #修改数据 print(obj1.name)
1、什么叫继承
继承是一种创建新类的方式,在Python中,新建的类可以继承一个或多个父类,新建的类可称为子类或派生类,父类又可称为基类或超类
注意:支持多继承
多继承:
优点:子类可以同时遗传多个父类的属性,最大限度的重用代码
缺点:继承表达的是一种什么是什么的关系,可能会跨种类继承,可读性会变差,扩展性变差,不建议使用多继承,如果真的涉及到多继承方面,应该使用Mixins机制
2、为什么要使用继承
可以解决类与类之间代码冗余问题
类时来解决对象与对象之间的问题
class Parent1(object): # 定义父类1 x = 1 class Parent2(object): # 定义父类2 pass class Sub1(Parent1): # 单继承 pass class Sub2(Parent1,Parent2): # 多继承 pass # print(Sub1.__bases__) # (<class '__main__.Parent1'>,) # print(Sub2.__bases__) # (<class '__main__.Parent1'>, <class '__main__.Parent2'>) # 在Python2中有经典类和新式类之分 # 新式类:继承了object类的子类,以及该子类的子类 # 经典类:没有继承了object类的子类,以及该子类的子类 # 在python3中,没有继承任何类,那么会默认继承object类,在Python3中所有的类都是新式类 # print(Parent1.__bases__) # (<class 'object'>,) # print(Parent2.__bases__) # (<class 'object'>,) # print(Sub1.x)
3、如何实现继承
要找出类与类之间的继承关系,需要先抽象,再继承。
# 类与类之间存在冗余问题,比如有相似的schoo # class Student: # school='OLDBOY' # # def __init__(self,name,age,sex): # self.name=name # self.age=age # self.sex=sex # # def choose_course(self): # print('学生%s 正在选课' %self.name) # # # class Teacher: # school='OLDBOY' # # def __init__(self,name,age,sex,salary,level): # self.name=name # self.age=age # self.sex=sex # self.salary=salary # self.level=level # # def score(self): # print('老师 %s 正在给学生打分' %self.name)
使用继承的方法后:
class OldboyPeople: school = 'OLDBOY' def __init__(self,name,age,sex): self.name=name self.age=age self.sex=sex class Student(OldboyPeople): # school='OLDBOY' # def __init__(self,name,age,sex): # self.name=name # self.age=age # self.sex=sex def choose_course(self): print('学生%s 正在选课' %self.name) stu_obj = Student('lili',18,'female') print(stu_obj.__dict__) stu_obj.choose_course() class Teacher(OldboyPeople): # school='OLDBOY' def __init__(self,name,age,sex,salary,level): # 在父类中只用到了一半的功能,还有自身的功能没有用上 OldboyPeople.__init__(self,name,age,sex) self.salary=salary self.level=level def score(self): print('老师 %s 正在给学生打分' %self.name) tea_obj = Teacher('andy',12,'male',1200,2) print(tea_obj.salary) print(tea_obj.__dict__)
属性查找
单继承背景下的属性查找
对象在查找属性时,先从对象自己的__dict__中找,如果没有则去子类中找,然后再去父类中找……
class Foo: def f1(self): print('Foo.f1') def f2(self): print('Foo.f2') self.f1() class Bar(Foo): def f1(self): print('Bar.f1') obj=Bar() obj.f2() # Foo.f2 # Bar.f1
obj.f2()会在父类Foo中找到f2,先打印Foo.f2,然后执行到self.f1(),即obj.f1(),仍会按照:对象本身->类Bar->父类Foo的顺序依次找下去,在类Bar中找到f1,因而打印结果为Foo.f1
父类如果不想让子类覆盖自己的方法,可以采用双下划线开头的方式将方法设置为私有的
class Foo: def __f1(self): # _Foo__f1 print('Foo.f1') def f2(self): print('Foo.f2') self.__f1() # self.__Foo_f1 class Bar(Foo): def __f1(self): # _Bar__f1 print('Bar.f1') obj=Bar() obj.f2()
继承的实现原理
菱形问题
一个子类是可以同时继承多个父类的,这就带来一个子类可以对多个不同父类加以重用的好处,但也有可能引发著名的 Diamond problem菱形问题(或称钻石问题,有时候也被称为“死亡钻石”),菱形其实就是对下面这种继承结构的形象比喻
用代码来表示:
class A: pass class B(A): def test(self): print('from B') class C(A): def test(self): print('from C') class D(B,C): pass
python到底是如何实现继承的呢?
定义的每一个类,Python都会计算出一个方法解析顺序(MRO)列表,该MRO列表就是一个简单的所有基类的线性顺序列表
class A: pass class B(A): def test(self): print('from B') class C(A): def test(self): print('from C') class D(B,C): pass # 对于定义的每一个类,Python都会计算出一个方法解析顺序(MRO)列表 print(B.mro()) obj1 = D() obj1.test() # [<class '__main__.B'>, <class '__main__.A'>, <class 'object'>] # from B
非菱形继承下,Python2余Python3的属性查找,都是一样,都是一个分支一个分支的找下去,最后找object
class E: def test(self): print('from E') class F: def test(self): print('from F') class B(E): def test(self): print('from B') class C(F): def test(self): print('from C') class D: def test(self): print('from D') class A(B, C, D): # def test(self): # print('from A') pass print(A.mro()) ''' [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class 'object'>] ''' obj = A() obj.test() # 先找到A,如果A没有就会在B中找,如果B没有,就会到B继承的E中找,依次类推,直到找到位置。
如果多继承是菱形类,经典类与新式类的属性查找顺序不一样;
经典类:深度优先,会在检索第一条分支的时候就直接一条道走到黑
新式类:广度优先,会在检索最后一条分支的时候检索最后一条总的类
深度优先
class G: # 在python2中,未继承object的类及其子类,都是经典类 def test(self): print('from G') class E(G): def test(self): print('from E') class F(G): def test(self): print('from F') class B(E): def test(self): print('from B') class C(F): def test(self): print('from C') class D(G): def test(self): print('from D') class A(B,C,D): # def test(self): # print('from A') pass obj = A() obj.test() # 如上图,查找顺序为:obj->A->B->E->G->C->F->D->object # 可依次注释上述类中的方法test来进行验证,注意请在python2.x中进行测试
广度优先
class G(object): def test(self): print('from G') class E(G): def test(self): print('from E') class F(G): def test(self): print('from F') class B(E): def test(self): print('from B') class C(F): def test(self): print('from C') class D(G): def test(self): print('from D') class A(B,C,D): # def test(self): # print('from A') pass obj = A() obj.test() # 如上图,查找顺序为:obj->A->B->E->C->F->D->G->object # 可依次注释上述类中的方法test来进行验证
总结:
多继承可以使用,但是继承结果尽量不要过于复杂,要满足继承的什么是什么的关系。
mixins机制
1、用来解决多继承的问题的
2、mixins机制的核心:就是在多继承背景下尽可能提升多继承的可读性
class Vehicle: pass class FlyableMixin: # 给这个类添加一个Mixin的后缀名,表示这是给其他类额外添加的一个功能 # 过于飞行有关的都可以放在这里面 def fly(self): pass class CivilAircraft(FlyableMixin,Vehicle): # 民航飞机 pass class Helicopter(FlyableMixin,Vehicle): # 直升飞机 pass class car(Vehicle): # 汽车 汽车与飞机还是有不一样的,汽车没有飞的功能 pass
注意:
1、 如果有多个功能,那就写多个Mixin类,一个类可以继承多个Mixin
2、 子类即便没有继承这个Mixin类,也照样可以工作,就是缺少了某个功能
3、 它不依赖于子类的实现
4、 它必须表示某一种功能,而不是某个物品,python 对于mixin类的命名方式一般以 Mixin, able, ible 为后缀
派生和方法重用
在子类派生的新方法中如何重用父类的功能
方式一:
指名道姓的调用每一个类下的函数,不依赖于继承关系
class OldboyPeople: def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex def f1(self): print('%s say hello' %self.name) class Teacher(OldboyPeople): def __init__(self,name,age,sex,level,salsry): OldboyPeople.__init__(self,name,age,sex) self.level = level self.salary = salsry tea_obj=Teacher('andy',12,'male',10,3000) print(tea_obj.__dict__)
方式二:
super()调用父类提供给自己的方法,严格依赖继承关系
调用super()会得到一个特殊的对象,该对象专门用来引用父类的属性,且严格按照MRO规定的顺序向后查找
class OldboyPeople: def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex def f1(self): print('%s say hello' %self.name) class Teacher(OldboyPeople): def __init__(self,name,age,sex,level,salsry): super().__init__(name,age,sex) # python3中科院简写 # super(Teacher,self).__init__(name,age,sex) Python2要写全 self.level = level self.salary = salsry tea_obj=Teacher('andy',12,'male',10,3000) print(tea_obj.__dict__)
# 调用super会得到一个特殊的对象,该对象会参照发起属性查找的那个类的mro,去当前类的父类中去找属性 class A: def test(self): print('from A') super().test() class B: def test(self): print('from B') class C(A,B): pass obj = C() obj.test() # from A # from B # print(C.mro()) # [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
注意:以上的两种方法都可以使用,但是不要混着使用
组合:
在一个类中以另外一个类的对象作为数据属性,称为类的组合。
1、什么是多态?
多态指的是一类事物有多种形态,比如动物有多种形态:猫、狗、猪
2、为什么要有多态?
多态性指的是可以在不考虑对象具体的情况下,而直接去使用对象
class Animail: def say(self): print('我会:',end='') class People(Animail): def say(self): super(People, self).say() print('呜呜呜') class Dog(Animail): def say(self): super(Dog, self).say() print('汪汪汪') class Pig(Animail): def say(self): super(Pig, self).say() print('昂昂昂') obj1=People() obj2=Dog() obj3=Pig() # obj1.say() # obj2.say() # obj3.say() # 也可以定义一个统一的接口 def talk(animal): animal.say() talk(obj1) # 内置了一个__len__()的方法,这也是多态的一种体现 # print('hello'.__len__()) # print([1,2,3].__len__()) # def my_len(val): # return val.__len__() # # print(my_len('hi'))
其实完全可以不依赖于继承,只需要制造出外观和行为相同对象,同样可以实现不考虑对象类型而使用对象,这正是Python崇尚的“鸭子类型”(duck typing):“如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子”。比起继承的方式,鸭子类型在某种程度上实现了程序的松耦合度
#二者看起来都像文件,因而就可以当文件一样去用,然而它们并没有直接的关系 class Txt: #Txt类有两个与文件类型同名的方法,即read和write def read(self): pass def write(self): pass class Disk: #Disk类也有两个与文件类型同名的方法:read和write def read(self): pass def write(self): pass
绑定方法和非绑定方法:
绑定方法
@classmethod
1> 绑定给对象的方法:调用者是对象,自动传入的是对象
2> 绑定给类的方法:调用者是类,自动传入的是类
# 配置文件settings.py的内容 ip='127.0.0.1' port=3306 # 类方法的应用 import settings class Mysql: def __init__(self,ip,port): self.ip = ip self.port = port def func(self): print('%s %s' %(self.ip,self.port)) @classmethod # 将下面的函数装饰成绑定给类的方法 def from_conf(cls): return cls(settings.ip,settings.port) obj1=Mysql('12',8080) obj1.func()
非绑定方法 静态方法
@ staticmethod
没有绑定给任何人,调用者可以是类,可以是对象,没有自动传参的效果
class Mysql: def __init__(self,ip,port): self.nid =self.create_id() self.ip = ip self.port = port # 函数体不需要对象也不需要类,就可以使用静态方法 @staticmethod # 将下述函数装饰成一个静态方法 def create_id(x,y): print(x,y) import uuid return uuid.uuid1() obj1 = Mysql('12',8080) obj1.create_id(1,2)
# 绝对值 print(abs(-12)) # 布尔值 # 全部为真才会返回True。可迭代对象为空,也返回True print(all('1')) # True print(all([1,'aaa','1',''])) # False print(all([])) # True # 全部是假就会返回假,有一个是真的就会返回True,可迭代对象为空,返回False print(any([])) # False print(any([0,None,1])) # True # 进制转换 print(bin(11)) print(oct(11)) print(hex(11)) # 判断某一个变量对应的值可不可以被调用 def foo(): pass class Foo: pass print(callable(Foo)) # True print(callable(foo)) # True # ASCLL 值 print(chr(65)) print(ord('a')) # 不可变集合 s = frozenset({1,2,3}) # 10 ** 2 % 3 print(pow(10,2,3)) *** # 能够进行属性查找 class Foo: pass print(dir(Foo)) ***# 得到索引和对应的值 for i,v in enumerate(['q','v','c']): print(i,v) ***# 可以将余数和商放到一起 print(divmod(20,4)) # (5, 0) ***# 执行字符串中的表达式 res= eval('1+2') print(res) # 3 ***# 判读一个对象是不是一个类的实例,类型判断 class Foo: pass obj = Foo() print(isinstance(obj,Foo)) # True ***# 拉链函数 v1 = 'hello' v2 = [111,222,333,444,555] res = zip(v1,v2) print(list(res)) # [('h', 111), ('e', 222), ('l', 333), ('l', 444), ('o', 555)] ***# 如果文件里面有一堆的模块,需要导入不能使用import time __import__('time') x.sleep(3)