面向对象编程

面向过程:

  • 不是一门技术,是一种编程思想
  • 核心是过程 过程就是想干什么,再敢什么,最后干什么,机械式思维
  • 案例:
    • 把大象关进冰箱
      1. 打开冰箱
      2. 把大象放进去
      3. 关闭冰箱
  • 案例二
    • 优点:
      • 复杂的问题简单化,流程化
    • 缺点:
      • 扩展性差,可维护性差

应用场景:对扩展性要求不高

问题:

  • 实现用户注册:
    1. 输入用户名密码
    2. 验证参数
    3. 注册

面向对象:

  • 核心是 '对象' 二字

    1. 程序里面

      ​ 对象就是盛放数据属性和功能的容器,

    2. 现实中

      ​ 对象就是特征与技能的结合体

      Linux:一切皆文件

  • 优点:扩展性强

  • 缺点:编程复杂度高

  • 应用场景:对扩展性要求较高的场景,比如:qq,微信

  • 案例:

    ​ 西天取经

    ​ 学生选课内容

类和对象

对象:特征和节能的结合体

类:一系列对象相似的特征和相似的技能的结合体

强调:站在不同的角度,划分的分类是不一样的

问题: 先有类还是先有对象

  1. 现实中:

    必须先有对象才有类

    1. 程序中:

    必须先定义类,再调用类产生对象

专业语法

定义类

class 类名:
    psaa
    
def 函数名():
    pass
    
类名:  一般是首字母大写

类的定义阶段发生了什么是?

1. 立即执行类体代码
2. 产生了类的名称空间,把类里面定义的名字都扔到字典里
3. 把类的名称空间绑定为类名	类名-dic_

产生对象发生了几件事

​ 1. 产生一个空对象,并且把该空对象当作第一个参数传递

调用类的过程就是实例化,得到的对象就是一个实例

定制对象自己独有的属性

# 版本3


"""
    产生对象发生了几件事?
        1. 产生一个空对象,并且把该空对象当成第一个参数传递
"""

class Student():
    school = 'SH'

    # 初始化方法
    def __init__(stu_obj, name, age, gender, courses=[]):
        stu_obj.name = name  # stu1.__dict__['name'] = 'egon'
        stu_obj.age = age  # stu1.__dict__['age'] = 20
        stu_obj.gender = gender  # stu1.__dict__['gender'] = 'male'
        stu_obj.courses = courses  # stu1.__dict__['courses'] = []

    def choose_course(stu_dic, course):
        stu_dic['courses'].append(course)
        print("%s 选课成功 %s" % (stu_dic['name'], stu_dic['courses']))


    # print(123)


# 类的名称空间
# print(Student.__dict__)
stu1 = Student('egon', 18 ,'male')

print(stu1.__dict__)
# stu2 = Student()

# stu1.name = 'egon'      # stu1.__dict__['name'] = 'egon'
# stu1.age = 18           # stu1.__dict__['age'] = 20
# stu1.gender = 'male'    # stu1.__dict__['gender'] = 'male'
# stu1.courses = []       # stu1.__dict__['courses'] = []
#
#
# stu2.name = 'tom'      # stu2.__dict__['name'] = 'egon'
# stu2.age = 18           # stu2.__dict__['age'] = 20
# stu2.gender = 'male'    # stu2.__dict__['gender'] = 'male'
# stu2.courses = []       # stu2.__dict__['courses'] = []


#
# init(stu1, 'egon', 18, 'male')
# init(stu2, 'tom', 18, 'male')
#
#
# print(stu1.__dict__)
# print(stu2.__dict__)

属性查找

class Student():
    school = 'SH'
    # 初始化方法
    def __init__(stu_obj, name, age, gender, courses=[]):
        stu_obj.name = name  # stu1.__dict__['name'] = 'egon'
        stu_obj.age = age  # stu1.__dict__['age'] = 20
        stu_obj.gender = gender  # stu1.__dict__['gender'] = 'male'
        stu_obj.courses = courses  # stu1.__dict__['courses'] = []

    def choose_course(self, course):
        self.courses.append(course)
        print("%s 选课成功 %s" % (self.name, self.courses))


    # print(123)


# 类的名称空间
# print(Student.__dict__)
stu1 = Student('egon', 18 ,'male')
stu2 = Student('tom', 18 ,'male')

# print(stu1.__dict__)

# 1 类的属性查找
# print(Student.__dict__['school'])

# # Student.__dict__['xxx'] = 'xxx'
# del Student.__dict__['xxx']

# Student.xxx = 'xxx'
#
# del  Student.xxx

# Student.school = 'MMM'
# print(Student.school)

# print(Student.choose_course)
# Student.choose_course(stu1, 'python')


# 对象操作属性  点语法取值,先从自己对象中取,如果没有,在取类中取值
# print(stu1.school)
#
# stu1.xxx = 'x'
# del  stu1.xxx
# stu1.school = 'y'

# Student.school = 'x'
# print(stu1.school)


# 类中的属性是共享给所有对象的,但是,类中的方法是给对象用的, 并且,把对象自己当成第一个参数传递
# stu1.school = 'y'
# print(stu1.school)
#
# print(stu2.school)

stu1.choose_course('python')

绑定方法

绑定方法两种:
1.绑定给对象的
2.绑定给类的

绑定对象

绑定类

类方法是绑定给类的,类来调用, 把类名当做第一个参数传递

使用装饰器 @classmates

非绑定方法

在类中设定的函数,并未使用类和对象。 -- 不绑定给对象也不绑定给类 -- 调用方法参数该调用方法参数该怎么传就怎么传

  • 使用装饰器@staticmethod 静态方法
    • 在类的函数里即可以不使用类也可以不对象(类中的函数,默认会输入对象 )

隐藏属性

  1. 如何隐藏属性
class People():
    __county = 'CHINA'

    def __init__(self,name,age):
        self.name = name
        self.age = age


peo1 = People("ky",26)
print(peo1._People__county) # 取对象peo1的county 
print(People._People__county)
  1. 为什么要隐藏

​ 将数据隐藏起来这不是目的。隐藏起来然后对外提供操作该数据的接口,然后我们可以在接口附加上对该数据操作的限制,以此完成对数据属性操作的严格控制。

  • 取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱
  • 对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做
  • 隔离了复杂度,同时也提升了安全性
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()

a=ATM()
a.withdraw()

继承

面对对象的三大特征:

  • 封装
  • 继承
  • 多态
  1. 什么是继承?
  • 继承就是一种新建类的方式, 新建的类我们称为字类或者叫派生类, 被继承的类成为父类或者叫基类
  • 子类可以遗传父类的属性
  1. 为什么用继承?
  • 类是解决对象与对象之间的代码冗余的

  • 继承是解决类与类之间的代码冗余的

  1. 如何使用继承?
  • python支持多继承

  • 经典类:没有继承object类的子子孙孙类都是经典类

  • 新式类:继承了object以及该类的子子孙孙类都是新式类

  • python2中才区分新式类和经典类

  • python3中都是新式类

class Parent1:
    pass
class Parent2:
    pass
class Sub1(Parent1):
    pass
class Sub2(Parent1, Parent2):
    pass

print(Parent1.__base__)
print(Parent2.__base__)
print(Sub1.__bases__)
print(Sub2.__bases__)

新知识点:

  • 字类如何使用父类的属性
    • 方式1:指名道姓, 跟继承没有关系
    • 方式2:super() 严格依赖继承

单继承下的属性查找

​ 就是最基本的继承

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()

多继承下的属性查找

  • mro来确认继承的属性查找流程
    • python3中使用新式类,python2中两种都有
    • 经典类
    • 新式类

img

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中进行测试

img

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来进行验证

super和mro

super().__init__(*, ***)
等价
父类.__init__(self, *, ***)

super 不一定为父类

mro获取继承的查询列表

多态

多态:一种事物的多种状态

  • 水: 固定水,液体水,水蒸气
  • 动物: 猫, 狗, 猪

继承: 用于解决冗余问题

多态类的继承:不是让子类遗传父类的属性和方法,是用来给子类定制标准的

多态带来的特性:在不用考虑对象数据类型的情况下,直接调用对象的方法

class Animal: #同一类事物:动物
    def talk(self):
        pass
class Cat(Animal): #动物的形态之一:猫
    def talk(self):
        print('喵喵喵')
class Dog(Animal): #动物的形态之二:狗
    def talk(self):
        print('汪汪汪')
class Pig(Animal): #动物的形态之三:猪
    def talk(self):
        print('哼哼哼')

#实例化得到三个对象
>>> cat=Cat()
>>> dog=Dog()
>>> pig=Pig()

我们可以定义一个统一的接口来使用

>>> def Talk(animal):
...     animal.talk()
... 
>>> Talk(cat)
喵喵喵
>>> Talk(dog)
汪汪汪
>>> Talk(pig)
哼哼哼

​ 多态性的好处在于增强了程序的灵活性和可扩展性,比如通过继承Animal类创建了一个新的类,实例化得到的对象obj,可以使用相同的方式使用obj.talk()

​ 多态性的本质在于不同的类中定义有相同的方法名,这样我们就可以不考虑类而统一用一种方式去使用对象,可以通过在父类引入抽象类的概念来硬性限制子类必须有某些方法名

import abc

# 指定metaclass属性将类设置为抽象类,抽象类本身只是用来约束子类的,不能被实例化
class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod # 该装饰器限制子类必须定义有一个名为talk的方法
    def talk(self): # 抽象方法中无需实现具体的功能
        pass

class Cat(Animal): # 但凡继承Animal的子类都必须遵循Animal规定的标准
    def talk(self):
        pass

cat=Cat() # 若子类中没有一个名为talk的方法则会抛出异常TypeError,无法实例化

鸭子类型

​ 但其实我们完全可以不依赖于继承,只需要制造出外观和行为相同对象,同样可以实现不考虑对象类型而使用对象,这正是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

组合

一个对象拥有一个属性,该属性是另外一个对象

  • 组合与继承解决类与类之间的冗余问题:
    • 继承: 满足什么是什么的关系
      • 继承是一种“是”的关系,比如老师是人、学生是人,当类之间有很多相同的之处,应该使用继承
    • 组合: 满足什么有什么的关系
      • 组合则是一种“有”的关系,比如老师有生日,老师有多门课程,当类之间有显著不同,并且较小的类是较大的类所需要的组件时,应该使用组合
class Course:
    def __init__(self,name,period,price):
        self.name=name
        self.period=period
        self.price=price
    def tell_info(self):
        print('<%s %s %s>' %(self.name,self.period,self.price))

class Date:
    def __init__(self,year,mon,day):
        self.year=year
        self.mon=mon
        self.day=day
    def tell_birth(self):
       print('<%s-%s-%s>' %(self.year,self.mon,self.day))

class People:
    school='清华大学'
    def __init__(self,name,sex,age):
        self.name=name
        self.sex=sex
        self.age=age

#Teacher类基于继承来重用People的代码,基于组合来重用Date类和Course类的代码
class Teacher(People): #老师是人
    def __init__(self,name,sex,age,title,year,mon,day):
        super().__init__(name,age,sex)
        self.birth=Date(year,mon,day) #老师有生日
        self.courses=[] #老师有课程,可以在实例化后,往该列表中添加Course类的对象
    def teach(self):
        print('%s is teaching' %self.name)


python=Course('python','3mons',3000.0)
linux=Course('linux','5mons',5000.0)
teacher1=Teacher('lili','female',28,'博士生导师',1990,3,23)

# teacher1有两门课程
teacher1.courses.append(python)
teacher1.courses.append(linux)

# 重用Date类的功能
teacher1.birth.tell_birth()

# 重用Course类的功能
for obj in teacher1.courses: 
    obj.tell_info()

此时对象teacher1集对象独有的属性、Teacher类中的内容、Course类中的内容于一身(都可以访问到),是一个高度整合的产物

mixins机制

  • 分主类和辅类 继承辅类就有该辅类中的功能, 不影响子类使用
  • 命名方式,以Mixin,able 为结尾,
  • 一般辅类的位置在主类的左边

异常

  1. 什么是异常

​ 异常就是错误发生的信号,当遇到该信号就会抛出异常, 如果不对该异常做处理,那么, 此后的代码都不再执行

  • 具体来说,异常分类两种:

    • 语法错误

    • 逻辑错误: 尽量把逻辑写到完美

  1. 为什么用异常?
    为了增强代码的可靠性,健壮性

  2. 如何使用异常

        try:
    # 被监测的代码1
    # 被监测的代码1
    pass
except 异常信息1 as e:
    # 捕捉到异常信息
except 异常信息2 as e
# 捕捉到异常信息
except 异常信息3 as e
    # 捕捉到异常信息
except 异常信息4 as e
    pass
except Exception as e:
    pass
else:
    print("当被监测的代码没有异常时候执行")
finally:
    print("不管有没有异常都会执行的")

自定义异常

class Myexception(BaseException):
    def __init__(self, msg):
        self.msg = msg

    def __str__(self):
        return "异常信息:%s" % self.msg

raise Myexception("出异常了哈哈哈哈")

import flask_session

断言

l = [1, 2, 3]
if len(l) < 5:
    # return
    # break
    raise Exception("断言失败。。。。")
断言
assert len(l) == 3
print("end---->")

内置函数

__init__()

面向对象class中的定义对象

__str__ 

只有在打印对象的时候才触发

​ 返回值必须是字符串

__del__
  1. 手动删除对象执行
  2. 应该程序执行完毕自动触发

反射

通过字符串操作类属性或者方法

posted @ 2021-07-12 20:01  Wkkforever  阅读(44)  评论(0)    收藏  举报