10.1面向对象思想
面向过程:
核心是“过程”二字
过程的终极奥义就是将程序流程化
过程就是“流水线”,分步骤来解决问题
面向对象:
核心是“对象”二字
对象的终极奥义就是将程序整合 对象是“容器”,用来盛放数据与功能的程序=数据+功能
一种数据和功能整合起来就是面向对象思想编程列表也可看做对象
10.2类与对象
什么是类?
类也是容器,该容器用来存放 同类对象 共有的数据与功能
# 在程序中先定义类,在调用类产生对象
# 一、先定义类
# 类式对象相似数据与功能的集合体
# 所以,类体中常见的代码是变量和函数的定义,但是类体其实是可以包含任意其他代码的
# 注意:类体代码是在类定义阶段就会立即执行,会产生类的名称空间
# 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'>
10.3封装
面向对象编程有三大特性:封装、继承、多态,其中最重要的一个特性就是封装。
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)
10.4继承
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'>]
注意:以上的两种方法都可以使用,但是不要混着使用
组合:
在一个类中以另外一个类的对象作为数据属性,称为类的组合。
10.5多态
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)
10.6内置函数
# 绝对值
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)
原创文章,作者:306829225,如若转载,请注明出处:https://blog.ytso.com/276332.html