一、面向对象简介

1. 编程思想

  • 总体上的编程思想有2种,一个是面向对象的编程思想,一个是面向过程的编程思想。

面向对象编程:Object Oriented Programming,简称OOP,是一种程序设计思想。
实际上,能够使用面向对象编程思想实现的程序,也都能通过面向过程完成。只是看哪种思想更适合当前开发需求。

  • 面向过程与面向对象区别

面向过程:根据业务逻辑从上到下写代码,符合一般的逻辑思维顺序。
面向对象:将数据与函数绑定到一起,进行封装。减少重复代码的重写过程。

2. 面向对象概念及术语

类(Class): 用来描述具有相同属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。其中的对象被称作类的**实例
对象:也称实例。通过类定义的初始化方法,赋予具体的值,成为一个”有血有肉的实体”。
实例化:创建类的实例的过程或操作。类似于函数的调用,然后赋值给一个新的变量,就是一个实例化的过程。
实例变量:定义在实例中的变量,只作用于当前实例。
类变量:类变量是所有实例公有的变量。类变量定义在类中,但在方法体之外。
数据成员:类变量、实例变量、方法、类方法、静态方法和属性等的统称。
方法:类中定义的函数。
• 静态方法:不需要实例化就可以由类执行的方法
• 类方法:类方法是将类本身作为对象进行操作的方法。
• 方法重写:如果从父类继承的方法不能满足子类的需求,可以对父类的方法进行改写,这个过程也称override。
封装:将内部实现包裹起来,对外透明,提供api接口进行调用的机制
继承:即一个派生类(derived class)继承父类(base class)的变量和方法。
多态:**根据对象类型的不同以不同的方式进行处理。

1. 类与对象介绍

类是抽象的概念,仅仅是模板。用来描述具有相同属性和方法的对象的集合。比如:”人”是一个类。

  • 对象

某一个具体事物的存在 ,在现实世界中可以是看得见摸得着的。 比如:”胡歌”就是一个对象。
类与对象的关系,我们可以进行对象归类。比如:分析班级同学公有特征归类。
点击查看【processon】

2. 类的构成

类由3个部分构成
• 类的名称:类名
• 类的属性:一组数据
• 类的方法:允许对类进行操作的方法
注意:类名通常采用驼峰式命名方式,尽量让字面意思体现出类的作用。

3. 类的表达式:Python使用class关键字来定义类,其基本结构如下:

  1. class 类名:
  2. pass

3. 创建对象

python中,可以根据已经定义的类去创建出一个个对象,创建对象的格式为 :

  1. 对象名 = 类名() #这又是一个实例化过程,对象名也叫做实例
  1. # 定义
  2. class 类名:
  3. def 方法名(self,参数): # 类中函数:称为方法
  4. pass
  5. # 执行
  6. s = 类名() # 创建对象(实例) 整个过程就是实例化
  7. s.方法名(参数) # 调用类中方法

1. self参数

在类当中定义方法时,会发现系统帮我们自动创建了self参数,并且在调用对象的该方法时,也无需传入self参数。那这个self是什么?实际上,我们需要明确self的两个概念
• self本身是形参
• self就是对象本身

  • 练习:定义类为学生类,创建对象李四,在类中定义方法打印李四信息
    1. class Student:
    2. def info(self):
    3. print(ls.name,ls.age)
    4. ls = Student()
    5. ls.name = "李四"
    6. ls.age = 18
    7. ls.info()
    8. #out
    9. 李四 18
  1. class LogicStudent: #类名
  2. def test(self): #方法名
  3. print(self) #类的内部函数叫做方法
  4. ls = LogicStudent() #ls叫做实例/对象,这个过程叫做实例化
  5. ls.test() #通过 对象名称.方法名(参数) 进行调用
  6. print(ls)
  7. print('-'*100)
  8. zs =LogicStudent() #当对象/实例是谁,self就是谁
  9. zs.test()
  10. print(zs)
  11. #out
  12. <__main__.LogicStudent object at 0x0000000001EAA208>
  13. <__main__.LogicStudent object at 0x0000000001EAA208>
  14. ----------------------------------------------------------------------------------------------------
  15. <__main__.LogicStudent object at 0x0000000001EAABE0>
  16. <__main__.LogicStudent object at 0x0000000001EAABE0>

在这个过程当中,对象将自身信息在类的外部定义赋值,封装到了类的内部
点击查看【processon】

当我再想创建一个对象,并且打印出对象信息时,发现如果这样做不灵活。

  1. class LogicStudent:
  2. def test(self):
  3. print(ls.name,ls.age)
  4. ls = LogicStudent()
  5. ls.name = '李四'
  6. ls.age = 18
  7. ls.test()
  8. print(ls)
  9. print('-'*100)
  10. zs =LogicStudent()
  11. zs.name = '张三'
  12. zs.age = 20
  13. zs.test()
  14. print(zs)
  15. #out
  16. 李四 18
  17. <__main__.LogicStudent object at 0x00000000021EA208>
  18. ----------------------------------------------------------------------------------------------------
  19. 李四 18
  20. <__main__.LogicStudent object at 0x00000000021EAA58>

所以优化如下:

  1. class LogicStudent:
  2. def test(self):
  3. print(self.name,self.age)
  4. ls = LogicStudent()
  5. ls.name = '李四'
  6. ls.age = 20
  7. ls.test()
  8. print(ls)
  9. print('-'*100)
  10. zs =LogicStudent()
  11. zs.name = '张三'
  12. zs.age = 20
  13. zs.test()
  14. print(zs)
  15. #out
  16. 李四 20
  17. <__main__.LogicStudent object at 0x00000000026AA208>
  18. ----------------------------------------------------------------------------------------------------
  19. 张三 20
  20. <__main__.LogicStudent object at 0x00000000026AAA58>

但是这样仍然有缺陷,大家会发现,用户信息暴露在类的外部

2. init()方法

init()方法称为初始化方法,也可称为构造方法。在创建对象时,会自动执行该方法,为对象的属性设置初始值。
init()方法不能自定义返回值
init()方法中声明的属性是允许在类的外部进行访问的

  1. class Student():
  2. def __init__(self,name,age):
  3. self.name = name
  4. self.age = age
  5. def test(self):
  6. print(self.name,self.age)
  7. self.gender = 'female'
  8. t = Student('张三',18) #实例化类,实际上t-->self
  9. t.test()
  10. print(t.name) #既然t-->self,那么t.name -->self.name,从而实现了访问
  11. print(t.age)
  12. print(t.gender)
  13. #out
  14. 张三 18
  15. 张三
  16. 18
  17. female
  1. class Student():
  2. def __init__(self):
  3. print('1')
  4. t = Student()
  5. print('2')
  6. #out
  7. 1
  8. 2

上题优化代码如下:

  1. class Student:
  2. def __init__(self,name,age): #形参接收
  3. self.name = name
  4. self.age = age
  5. def test(self):
  6. print(self.name,self.age)
  7. ls = Student("张三",18) #实参传入
  8. ls.test()
  9. #out
  10. 张三 18

3. str()方法

如果在开发中,希望打印输出对象变量时,能够打印自定义的内容。就可以使用str()方法,将自定义内容通过return关键字返回。

  1. class Student:
  2. def __init__(self,name,age):
  3. self.name=name
  4. self.age=age
  5. def __str__(self):
  6. return f'{self.name},{str(self.age)}'
  7. wei =Student('wei',18)
  8. print(wei)
  9. wang =Student('wangwu',22)
  10. print(wang)
  11. print(id(wei))
  12. print(id(wang))
  13. print(id(Student))
  14. #out
  15. wei,18
  16. wangwu,22
  17. 38819264
  18. 38819320
  19. 37563656
  • 注意:返回值必须是字符串。

**

4. 私有属性与私有方法

1. 私有属性定义

  • 私有属性:就是对象不希望公开的属性
  • 定义方式:在属性名前面增加两个下划线(例如:__name)
  • 练习

• 定义类为:”人”类 • 创建对象:rose • 初始化对象属性:name与age • 要求:age不能在类的外部访问

  1. class Person:
  2. def __init__(self):
  3. self.name = 'rose'
  4. self.__age = 18
  5. a = Person()
  6. print (a.name)
  7. print (a.age)
  8. #out
  9. AttributeError: 'Person' object has no attribute 'age'
  10. rose

2. 私有属性访问

那如果有需求一定要在类的外部访问到对象的私有属性时,我们可以使用如下步骤:

  • 可以通过类的内部的方法访问私有属性
  • 通过类的内部方法将私有属性返回出去
  • 在类的外部调用该方法并且接收就可访问
    1. class Person:
    2. def __init__(self):
    3. self.name = 'rose'
    4. self.__age = 18
    5. def test(self):
    6. return self.__age
    7. a = Person()
    8. print (a.name)
    9. a_age = a.test()
    10. print(a_age)
    11. #out
    12. rose
    13. 18

3. 私有方法定义

  • 私有方法:就是对象不希望公开的方法
  • 定义方式:在方法名前面增加两个下划线(例如:__test),代码如下:

    1. class Test:
    2. def tes1(self):
    3. print("--1--")
    4. def __tes2(self):
    5. print("--2--")
    6. def tes3(self):
    7. print("--3--")
  • [x] 注意

在Python中,并没有真正意义上的私有,只有伪私有。当我们在访问时,以对象.类名_私有属性名称即可访问,私有方法同理。代码如下:

  1. class Test:
  2. def __init__(self,name,age):
  3. self.name=name
  4. self.__age=age
  5. wei=Test('wei',22)
  6. print(wei.name)
  7. print(wei._Test__age)
  8. print(id(wei))
  9. print(id(wei.name))
  10. print(id(wei._Test__age))
  11. #out
  12. wei
  13. 22
  14. 35476256
  15. 35453056
  16. 505704576

同样也需要注意的是,在实际开发中,不要通过该方式访问对象的私有属性或者私有方法

  • 玩个特别无聊的游戏,猜数字。 玩家输入一个数字与 计算机随机生成的数字作对比,当两个值相等时,则说明用户猜对了。注意:外部不能获取到计算机随机生成的值。 ```python import random class GuessNum: def init(self):
    1. self.__num = random.randint(1,6)
    def test(self):
    1. test_num = int(input("请输入:"))
    2. while True:
    3. if self.__num == test_num:
    4. print("恭喜你 猜对了")
    5. break
    6. else:
    7. test_num = int(input("你猜错了,请重新输入:"))
    t=GuessNum() t.test()

out

请输入:1 你猜错了,请重新输入:2 你猜错了,请重新输入:3 你猜错了,请重新输入:4 你猜错了,请重新输入:5 你猜错了,请重新输入:6 恭喜你 猜对了

  1. - [x] 《寻欢作乐》《面纱》《月亮与六便士》《刀锋》...这些书籍的作者都是毛姆(以最节约内存的方式),并且管理员想统计每本书籍阅读的时长。使用面向对象的方式输出毛姆:《书籍名》总共阅读:**小时
  2. ```python
  3. import time
  4. class BookMan:
  5. book_authr='毛姆'
  6. def __init__(self,name):
  7. self.name=name
  8. self.read_time=0
  9. def reading_time(self,num):
  10. self.read_time+=num
  11. def print_info(self):
  12. print('{}:{},总计阅读时长为:{}小时'.format(BookMan.book_authr,self.name,self.read_time))
  13. @classmethod
  14. def set_authr(cls):
  15. cls.book_authr='魏先生'
  16. print(cls.book_authr)
  17. @staticmethod
  18. def get_systime():
  19. print(time.strftime("%H:%M:%S",time.localtime()))
  20. book1 = BookMan("《刀锋》")
  21. book2 = BookMan("《月亮与六便士》")
  22. book3 = BookMan("《寻欢作乐》")
  23. book4 = BookMan("《面纱》")
  24. book1.reading_time(2)
  25. book1.reading_time(2)
  26. book2.reading_time(2)
  27. book3.reading_time(2)
  28. book4.reading_time(2)
  29. book1.print_info()
  30. book4.print_info()
  31. BookMan.set_authr()
  32. BookMan.get_systime()
  33. #out
  34. 毛姆:《刀锋》,总计阅读时长为:4小时
  35. 毛姆:《面纱》,总计阅读时长为:2小时
  36. 魏先生
  37. 14:42:11
  • 创建一个煎饼类,调用烹饪时长的方法累计煎饼状态:如果煎的时间在0-3之间则状态为生的;如果煎的时间在3-5之间,则状态为半生不熟的;如果煎的时间在5-8之间,则状态为全熟的;当时间超过 8 分钟,状态焦了。并且还可以给煎饼添加作料,比如大葱(hhh)、大蒜(hhh)、烤肠等等
    1. class Pancake:
    2. def __init__(self):
    3. self.cookState='生的'
    4. self.cookLevel=0
    5. self.addState=[]
    6. def __str__(self):
    7. return '煎饼的状态{},烤的时间为{},添加的辅料为{}'.format(self.cookState,self.cookLevel,self.addState)
    8. def cook(self,cooktime):
    9. self.cookLevel +=cooktime
    10. if self.cookLevel>=0 and self.cookLevel<3:
    11. self.cookState='生的'
    12. elif self.cookLevel>=3 and self.cookLevel<5:
    13. self.cookState='半生不熟'
    14. elif self.cookLevel>=5 and self.cookLevel<8:
    15. self.cookState='全熟'
    16. elif self.cookLevel >= 8:
    17. self.cookState = '糊了'
    18. def add_addState(self,food):
    19. self.addState.append(food)
    20. pancake = Pancake()
    21. pancake.cook(1)
    22. pancake.add_addState('洋葱')
    23. #out
    24. 煎饼的状态生的,烤的时间为1,添加的辅料为['洋葱']

5. 销毁对象

对象什么时候销毁:没有被引用时就会自动销毁

  1. import sys
  2. a=1
  3. class Demo:
  4. pass
  5. d=Demo()
  6. dd = d
  7. print(sys.getrefcount(d))
  8. del dd
  9. print(sys.getrefcount(d))
  10. print(sys.getrefcount(a))
  11. #out
  12. 3
  13. 2
  14. 132

二、成员

1. 成员简介

类的成员可分为字段、方法以及属性。

2. 字段

1. 定义

上节课我们学习到的属性,更准确来说被称为字段。字段又主要可以分为普通(实例)字段/实例属性与静态(类)字段/类属性。

2. 普通字段

  • 普通字段属于对象,保存在对象中,只能通过对象访问。
  • 定义:self.字段名称
  • 访问:self.字段名称 或 对象.字段名称
  • 练习:定义一个省份类,打印输出班级每位同学所在的国家与省份。
    1. class Province:
    2. def __init__(self,country,name):
    3. self.country=country
    4. self.name=name
    5. def print_info(self):
    6. print(self.country,self.name)
    7. wei = Province('中国','安徽')
    8. wei.print_info()
    9. #out
    10. 中国 安徽

但是这样实现,会有相应的问题。比如说,普通字段保存在对象中,也就是说,当创建一个对象时,就会重新创建self.country以及self.name字段。当存对象的字段值相同时,仍然使用普通字段便会造成我们内存的浪费。
第七讲:面向对象 - 图1

3. 静态字段(类属性)

  • 静态字段属于类,保存在类中。在创建时,仅创建一份,并且所有对象都共享静态字段。执行时可以是类访问也可以是对象访问。
  • 定义:直接在类中定义
  • 访问:类名.字段名称self.字段名称对象.字段名称
  • 练习优化:
    1. class Province:
    2. country = '中国'
    3. def __init__(self,name):
    4. self.name=name
    5. def print_info(self):
    6. print(self.name,Province.country)
    7. wei = Province('安徽')
    8. wei.print_info()
    9. #out
    10. 安徽 中国

总结

在实际开发中,如果字段的值是固定的,不随对象的改变而改变。那就使用静态字段(类属性),这样有利于节省内存的消耗。而如果字段的值随对象的改变而改变,就使用普通字段(实例属性)

3. 方法

1. 分类

类的方法主要可分为普通方法(实例方法),类方法,静态方法。

2. 普通方法(实例方法)

普通方法保存在类中,在实例化对象后,一般通过对象调用。第一个参数必须为系统自建参数,默认为self,代指对象本身。注意:self仅仅是变量名。使用其它也可,但最好不要修改。

  • 定义及访问如下:

    1. class Test:
    2. def __init__(self):
    3. self.name = 'rose'
    4. def test(self):
    5. print(self.name)
    6. def test2(self):
    7. self.test() #在类的内部的其它普通方法中,通过self调用
    8. d.test() #在类的内部的其它普通方法中,通过对象调用
    9. t = Test()
    10. t.test()
    11. t.test2()
  • 应用场景:当方法中需要使用普通字段(实例对象)时,通常使用普通方法。

3. 静态方法

  • 静态方法通过在方法上面添加@staticmethod装饰器定义,保存在类中;
  • 静态方法不需要传入self参数,即使传入了self参数也并不像普通方法那样代指对象本身,仅仅是一个普通的形参。
  • 静态方法的调用虽然可以通过对象调用,但一般由类直接调用。定义及访问如下: ```python class Demo: def init(self):
    1. self.name = "rose"
    2. self.age = 18
    def test(self):
    1. print(self.name)
    @staticmethod # 通过@staicmethod装饰器将方法转为静态方法 def stat_test(): # 静态方法self不是必须加的,加了也不代表对象
    1. print("我是静态方法")
    rose = Demo() rose.test() Demo.stat_test() # 一般通过类名调用

class Demo: def init(self): self.name=’rose’ self.age=18 def test(self): print(self.name) @staticmethod def stat_test(t): #这个参数相当于函数的形参 print(‘我是静态方法’) print(t) d=Demo() Demo.stat_test(‘111’) #将参数传入函数类的方法中,也相当于是静态方法的传参

  1. - 应用场景
  2. 静态方法的参数并不代表对象这一特点,静态方法在类中使用起来更像一个单纯的函数。完全可以将静态方法放在类的外部,当成函数使用。但是放在类中,便于使用与维护。
  3. - [ ] **练习:在类中定义一个打印输出当前时间的方法**
  4. ```python
  5. import time
  6. def show_time():
  7. print(time.strftime("%H:%M:%S",time.localtime()))
  8. show_time()
  9. class Test:
  10. @staticmethod
  11. def show_time():
  12. print(time.strftime("%H:%M:%S", time.localtime()))
  13. t=Test()
  14. Test.show_time()

4. 类方法

  • 类方法通过在方法上面添加@classmethod装饰器,保存在类中;
  • 类方法不需要传入self。但有一个系统自建参数为cls,cls代指类本身;
  • 类方法一般通过类调用,也可通过对象调用。定义及访问如下 ```python class Demo: @classmethod # 通过@classmethod装饰器将方法转为静态 def clasmd(cls): # 自建cls参数 代指 当前类
    1. print(cls) # <class '__main__.Demo'>

d = Demo() Demo.clasmd() # 通过 类名 调用

  1. <a name="3DzOz"></a>
  2. #### 总结:
  3. ![](https://cdn.nlark.com/yuque/0/2020/png/704747/1584174612895-07131d6c-dbd3-46e0-9936-984604a5cceb.png#align=left&display=inline&height=217&margin=%5Bobject%20Object%5D&originHeight=217&originWidth=384&status=done&style=none&width=384)<br />一般而言<br />• 访问字段:对象.字段名称<br />• 访问方法:对象.方法名称([参数])
  4. <a name="Ivnni"></a>
  5. ### 4. 属性
  6. 在实际开发中,为了简便,当想访问方法以属性的访问形式时,就可以通过在方法上面添加@property装饰器,达到该效果。
  7. <a name="BR5Gn"></a>
  8. #### 1. 定义及访问如下
  9. ```python
  10. class Demo:
  11. def __init__(self):
  12. self.name = "rose"
  13. @property
  14. def test(self):
  15. print(self.name)
  16. d = Demo()
  17. d.test # 通过@property 以 普通属性 形式 访问 普通方法
  • 注意:以上@property只相当于一个只读属性,也就是说,仅能访问test函数的内容。

  • [x] 练习:需求1.通过传参去改变self.name的值;2.删除掉self.name。 ```python class Demo: def init(self):

    1. self.name = "rose"

    @property def test(self): # 只可访问模式

    1. print(self.name)

    @test.setter # d.set_test = “jack” def test(self,name):

    1. self.name = name
    2. print(self.name)

    @test.deleter def test(self): # del d.test

    1. del self.name
    2. print(self.name) # 报错 self.name已被删除

d = Demo() d.test # 通过@property 只读模式

改变self.name的值

d.test = “jack” # 给方法传参 以 赋值方式 会触发@test.setter

删除self.name

del d.test # del 会 触发 @test.deleter

  1. - [x] **练习:实现分页**
  2. 思路<br />• 用户输入查看的页面<br />• 比如输入1-->[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]<br />• 比如输入2-->[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]<br />• ...<br />面向过程代码
  3. ```python
  4. li = [i for i in range(1000)] # 比如一共100页
  5. while True:
  6. p = int(input("请输入要查看的页码:"))
  7. start = (p - 1) * 10
  8. end = p * 10
  9. print(li[start:end])

需求
• 使用面向对象编程思想实现
• 并且在类中定义start与end方法
• 通过调用start与end方法实现(尽量通过调用属性的方式实现)

  1. class PageGame:
  2. def __init__(self,page):
  3. try:
  4. page1 = int(page) # input默认为str,所以int强转
  5. except Exception as e:
  6. page1 = 1 # 万一有输入浮点..字母等会报错,多以捕捉异常,并且重新赋值为1
  7. self.page = page1 # 将处理后的p赋值给实例属性page
  8. @property
  9. def start(self):
  10. page = (self.page-1) * 10
  11. return page
  12. @property
  13. def end(self):
  14. page = self.page*10
  15. return page
  16. li = [i for i in range(1000)]
  17. while True:
  18. page = int(input("请输入要查看的页码:"))
  19. test = PageGame(page)
  20. print(li[test.start:test.end])
  21. break
  22. #out
  23. 请输入要查看的页码:2
  24. [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
  • 类中有一个私有属性,__age。想要在外部可以对其访问,改变,删除 ,使用 property 的 fget,fset,fdel。
    1. class Test(object):
    2. def __init__(self):
    3. self.__age=18
    4. def get_age(self):
    5. print(self.__age)
    6. return self.__age
    7. def set_age(self,age):
    8. self.__age=age
    9. print(self.__age)
    10. def del_age(self):
    11. print(self.__age)
    12. del self.__age
    13. print(self.__age)
    14. age=property(fget=get_age,fset=set_age,fdel=del_age)
    15. t=Test()
    16. t.age
    17. t.age='20'
    18. del t.age

三、面向对象特征

面向对象三大特性:封装,继承与多态

1. 封装

1. 封装简介:

封装是面向对象编程的一大特点,将属性和方法放到类的内部,通过对象访问属性或者方法,隐藏功能的实现细节,也可以设置访问权限。
点击查看【processon】

2. 封装代码演练:

  1. class Student:
  2. def __init__(self,name,age):
  3. self.name=name
  4. self.age=age
  5. def print_info(self):
  6. print(self.name,self.age)
  7. w = Student('wei',18)
  8. w.print_info()
  9. #out
  10. wei 18

2. 继承(重点)

1. 继承介绍

继承是一种创建新类的方式,如果子类需要用到父类的属性或者方法时,就可以使用继承。当然,子类也可以提供自己的属性和方法。

2. 继承格式

  1. class Father: #父类 或者超类、基类
  2. pass
  3. class Sun(Father): #子类 或者派生类
  4. pass

注意:在Python中,新建的类可以继承一个或多个父类,也就是说可以单继承,也可以多继承

3. 继承的作用

避免重复造轮子的行为,减少冗余代码

4. 新式类和经典类

在Python2当中类分为新式类和经典类,如果有继承父类object则是新式类,否则为经典类。
但是在Python3当中,全部都是新式类,默认继承object。

  • [x] 练习:使用代码演练验证py3中的新式类

    1. class Father: #py2中叫做经典类;py3中默认继承object,都为新式类
    2. pass
    3. class Son(object): #py2中叫做新式类
    4. pass
    5. f=Father()
    6. s=Son()
    7. print(len(f.__dir__()))
    8. print(len(s.__dir__()))
    9. #out
    10. 26 #26个方法
    11. 26 #26个方法

    5. 单继承

    子类继承父类,则可以直接享受父类中已经封装好的方法

  • [ ] 练习:用代码演练实现单继承的方法

    1. class GrandFather(object):
    2. def sleep(self):
    3. print('GrandFather sleep')
    4. class Father(GrandFather):
    5. def eat(self):
    6. print('Father eat')
    7. def drink(self):
    8. print('Father drink')
    9. class Son(Father):
    10. def study(self):
    11. print('Son study')
    12. s=Son()
    13. s.eat()
    14. s.study()
    15. s.sleep()
    16. #out
    17. Father eat
    18. Son study
    19. GrandFather sleep
  • 小结:

当对象调用方法时,查找顺序先从自身类找,如果自身没找到,则去父类找,父类无,再到父类的父类找,直到object类,若还无,则报错。这也称为深度优先机制。
当子类与父类拥有同名称的方法时,子类对象调用该方法优先执行自身的方法。那么实际上就是子类的方法覆盖父类的方法,也称为重写。
点击查看【processon】
在实际的开发中,遵循开放封闭原则。我们并不会完全的重写父类的方法,而是希望同时实现父类的功能。这时,我们就需要调用父类的方法了,可以通过super()函数实现。

  • super()函数

super()函数是用于调用父类(超类)的一个方法。

  • 语法

super(type[, object-or-type])
• type—>类
• object-or-type — 类,一般是 self

  • [x] 练习:继以上练习实现,在Son的sleep方法当中,调用父类的sleep方法。

    1. class GrandFather(object):
    2. def sleep(self):
    3. print('GrandFather sleep')
    4. class Father(GrandFather):
    5. def eat(self):
    6. print('Father eat')
    7. def drink(self):
    8. print('Father drink')
    9. def sleep(self):
    10. print('Father sleep')
    11. super().sleep()
    12. class Son(Father):
    13. def study(self):
    14. print('Son study')
    15. def sleep(self):
    16. print('Son sleep')
    17. super().sleep() #或者super(Son,self).sleep() 或者Father(self).sleep()
    18. s=Son()
    19. s.eat()
    20. s.study()
    21. s.sleep()
    22. #out
    23. Father eat
    24. Son study
    25. Son sleep
    26. GrandFather sleep
  • 小结

    • 继承 深度优先 先从自己找 找不到则往父类找
    • 重写(优先执行子类中的方法)
    • self永远是执行该方法的调用者
    • super(子类,self)调用父类中的方法(arg) 或者使用父类名.父类中的方法(self,arg),这里也是深度优先
  • 注意
    • init方法也会继承,同实例方法(普通方法)一致
    • 私有属性以及私有方法没有被继承
      1. class GrandFather(object):
      2. def __init__(self):
      3. print('我是GrandFather中的构造方法')
      4. def sleep(self):
      5. print('GrandFather sleep')
      6. class Father(GrandFather):
      7. def __init__(self):
      8. print('我是Father中的构造方法')
      9. def eat(self):
      10. print('Father eat')
      11. def drink(self):
      12. print('Father drink')
      13. def sleep(self):
      14. print('Father sleep')
      15. super().sleep()
      16. class Son(Father):
      17. def __init__(self):
      18. print('我是Son中的构造方法')
      19. def study(self):
      20. print('Son study')
      21. def sleep(self):
      22. print('Son sleep')
      23. super().sleep()
      24. s=Son()
      25. s.eat()
      26. s.study()
      27. s.sleep()
      28. #out
      29. 我是Son中的构造方法
      30. Father eat
      31. Son study
      32. Son sleep
      33. Father sleep
      34. GrandFather sleep
      1. class Fa(object):
      2. def __init__(self):
      3. self.name='wei'
      4. self.__age=20
      5. def test(self):
      6. print('Fa test')
      7. def __test2(self):
      8. print('Fa __test2')
      9. def test3(self):
      10. print(self.__age)
      11. self.__test2()
      12. class Son(Fa):
      13. def B_test(self):
      14. print(self.__age)
      15. s=Son()
      16. s.test()
      17. s.test3()
      18. #out
      19. Fa test
      20. 20
      21. Fa __test2

      6. 多继承

      所谓多继承,即子类有多个父类,并且具有它们的特征。遵循左边优先原则:

点击查看【processon】
代码演练如下:

  1. class F(object):
  2. def run(self):
  3. print('F run')
  4. class F1(object):
  5. def run(self):
  6. print('F1 run')
  7. class S(F1,F): #在这个位置进行顺序执行
  8. pass
  9. s=S()
  10. s.run()
  11. #out
  12. F1 run
  1. class GF(object):
  2. def run(self):
  3. print('GF run')
  4. class F(GF):
  5. pass
  6. class F1(object):
  7. def run(self):
  8. print('F1 run')
  9. class S(F,F1):
  10. pass
  11. s=S()
  12. s.run()
  13. #out
  14. GF run
  1. class GF(object):
  2. def run(self):
  3. print('GF run')
  4. class F(GF):
  5. pass
  6. class F1(GF):
  7. def run(self):
  8. print('F1 run')
  9. class S(F,F1):
  10. pass
  11. s=S()
  12. s.run()
  13. #out
  14. F1 run
  • 小结:基于C3算法,设计成如下执行顺序

    1. print(S.__mro__)
    2. #out
    3. (<class '__main__.S'>, <class '__main__.F'>, <class '__main__.F1'>, <class '__main__.GF'>,
    4. <class 'object'>)

    3. 多态

    1. 概念

    多态的概念是应用于Java和C#这一类强类型语言中,而Python崇尚”鸭子类型”
    动态语言调用实例方法时不检查类型,只要方法存在,参数正确,就可以调用。这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。
    所谓多态:定义时的类型和运行时的类型不一样,此时就成为多态。

    2. 多态演练

    1. class Person(object):
    2. def print_info(self):
    3. print('打印一条信息。。。')
    4. class Man(Person):
    5. def print_info(self):
    6. print('。。。另外一条自我介绍')
    7. def print_info(obj):
    8. obj.print_info()
    9. wei=Person()
    10. print_info(wei)
    11. wang=Man()
    12. print_info(wang)
    13. #out
    14. 打印一条信息。。。
    15. 。。。另外一条自我介绍

    4. 常用魔法方法

    1. 魔法方法简介

    在Python中,有一些内置好的特定方法,这些方法在进行特定的操作时会自动被调用,称为魔法方法。
    魔法方法的命名总是被双下划线包围,比如名称

  • doc

doc用来查看类的说明文档

  • [ ] 练习,查看列表类的说明文档

    1. print(list.__doc__)
    2. #out
    3. list() -> new empty list
    4. list(iterable) -> new list initialized from iterable's items

    查看自定义类的说明文档 ```python class Demo(object): “”” 我是用来检测 类的说明文档的 “”” pass d=Demo() print(d.doc)

    out

    我是用来检测 类的说明文档的

  1. - **__module__**
  2. __module__用来查看当前操作的类所在模块,一个.py文件其实就是一个模块
  3. - [ ] 练习:创建A.pyB.py,在A.py中定义类Demo,在A.py中输出Demo.__module__,在B.py中导入A中的Demo类运行B.py
  4. - **总结**
  5. 当执行模块是类所在模块时,执行结果为__main__。否则,执行结果是类所在模块的名称。
  6. - **__class__**
  7. __class__用来查看当前对象的类
  8. - [ ] 练习
  9. 创建Demo类<br />• 创建Demo对象为d<br />• 查看Demo类的__class__<br />• 查看Demo对象d__class__
  10. ```python
  11. class Demo(object):
  12. """
  13. 我是用来检测 类的说明文档的
  14. """
  15. pass
  16. d=Demo()
  17. print(Demo.__class__)
  18. print(d.__class__)
  19. #OUT
  20. <class 'type'>
  21. <class '__main__.Demo'>
  • 总结

类也是对象,Demo是type类的对象,d是Demo类的对象。

  • dict

dict用于获取类或者实例的属性字典
注意
• 普通字段存储在对象中,所以通过对象.dict获取的是普通字段
• 除普通字段以外的成员都存储在类中,所以通过类.dict来获取

  1. class Students(object):
  2. def __init__(self):
  3. self.name='wei'
  4. self.__age=18
  5. def test(self):
  6. self.gender='male'
  7. print('test')
  8. wei=Students()
  9. print(wei.__dict__)
  10. #out
  11. {'name': 'wei', '_Students__age': 18}
  12. print(Students.__dict__)
  13. #OUT
  14. {'__module__': '__main__', '__init__': <function Students.__init__ at 0x00000000028A1F28>
  15. , 'test': <function Students.test at 0x00000000028AA2F0>, '__dict__': <attribute '__dic
  16. t__' of 'Students' objects>, '__weakref__': <attribute '__weakref__' of 'Students' obje
  17. cts>, '__doc__': None}
  • del()方法

del()方法也叫做析构方法。当由该类创建的实例对象,被删除或者说在内存中被释放,将会自动触发执行。
总结
• 当代码全部执行完毕才自动触发del()
• 如果需要提前触发,则需要通过del关键字,删除所有对象后触发del()

  1. class Demo(object):
  2. def __del__(self):
  3. print('我被回收了')
  4. d=Demo()
  5. print('--'*50)
  6. print('--'*50)
  7. #out
  8. ----------------------------------------------------------------------------------------------------
  9. ----------------------------------------------------------------------------------------------------
  10. 我被回收了
  11. class Demo(object):
  12. def __del__(self):
  13. print('我被回收了')
  14. d=Demo()
  15. print('--'*50)
  16. del d
  17. print('--'*50)
  18. #out
  19. ----------------------------------------------------------------------------------------------------
  20. 我被回收了
  21. ----------------------------------------------------------------------------------------------------

注意
此方法一般不需要定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配与释放,一般都是交给Python解释器来执行。所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行。

  • call()方法

call()方法用于将对象变成一个可调用的对象。也就是说,当一个类中有call()方法时,其实例化得到的对象便是可调用的(callable)

  1. class Demo(object):
  2. def __call__(self, *args, **kwargs):
  3. print('可被调用了')
  4. pass
  5. d=Demo()
  6. print(d)
  7. d()
  8. #out
  9. <__main__.Demo object at 0x0000000001E9F9E8>
  10. 可被调用了
  • new()方法

new()方法用于创建与返回一个对象。在类准备将自身实例化时调用

  • 练习:以下代码打印输出的顺序?

A.init,new
B.init
C.new
D.new,init__

  1. class Demo(object):
  2. def __init__(self):
  3. print('__init__')
  4. def __new__(cls, *args, **kwargs):
  5. print('__new__')
  6. d=Demo()
  7. #out
  8. __new__

注意
new()方法用于创建对象
init()方法在对象创建的时候,自动调用
• 但是此处重写了父类的new()方法,覆盖了父类new()创建对象的功能,所以对象并没有创建成功。所以仅执行new()方法内部代码

  • 对象创建执行顺序

• 1.通过new()方法创建对象
• 2.并将对象返回,传给init()

  • 练习:在自定义类中实现创建对象

思路
• 重写父类new()方法
• 并且在该方法内部,调用父类的new()方法

  1. class Demo(object):
  2. def __init__(self):
  3. print('__init__')
  4. def __new__(cls, *args, **kwargs):
  5. print('__new__')
  6. return super().__new__(cls)
  7. d=Demo()
  8. #out
  9. __new__
  10. __init__

注意
• 在创建对象时,一定要将对象返回,才会自动触发init()方法
init()方法当中的self,实际上就是new返回的实例,也就是该对象
init()与new()区别
init实例方法,new静态方法
init在对象创建后自动调用,new创建对象的方法

2.单例模式

思路
• 当对象不存在时,创建对象
• 当对象存在时,永远返回当前已经创建对象