Python进阶编程:面向对象编程

面向对象

根据B站大学廖雪峰的python整理而来

什么是面向对象

面向过程:思考的重点在于步骤。

面向对象:把问题分解成各个对象,描述对象在整个事情的行为。

引子

  • 人狗大战

    • 先创建很多狗,每个狗有各自的名字,品种,攻击力等

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      # 面向过程的思考
      dog1 = {
      "name": "小红", # 创建dog1
      "d_type": "京巴",
      "attack_val": 40
      }

      def bite1(person): # dog1咬人
      person.life_val -= 40


      dog2 = {"name": "小明", # 创建dog2
      "d_type": "牧羊犬",
      "attack_val": 50
      }

      def bite2(person): # dog2咬人
      person.life_val -= 50

      print(dog1)
      print(dog2)

      #以此类推创建dog3,doe4......
      #可以看到每当创建一个dog3就要重复很多代码,很是麻烦,有没有更好的办法的呢?
      #很负责任的跟大家说—— 没有
      #是不可能的!!
      #下面就是另一种编程思想

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      # 面向对象的思考
      #写一个函数,包含了每个狗的共同点
      attack_vals = {
      "京巴":40,
      "牧羊犬":50,
      "藏獒":60
      }

      def dog(name, d_type):
      data = {
      "name": name,
      "d_type": d_type,
      "life_val": 80
      }

      if d_type in attack_vals: # 根据品种添加攻击力
      data["attack_val"] = attack_vals[d_type]

      return data


      dog1 = dog("小红", "京巴") # 实体1
      dog2 = dog("小明", "牧羊犬") # 实体2

      print(dog1)
      print(dog2)

    • 创建很多人

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      #人也按照共同点创造
      def person(name, age):
      data = {
      "name": name,
      "age": age,
      "life_val": 100
      }

      if age > 18:
      data["attack_val"] = 50
      else:
      data["attack_val"] = 20

      return data


      P1 = person("阿福", 21)
      P2 = person("小璇", 17)
      P3 = person("小岚", 21)
      P4 = person("小乐", 21)

      print(P1)
      print(P2)
      print(P3)
      print(P4)

    • 人可以打狗,狗可以打人

      1
      2
      3
      4
      5
      6
      7
      8
      #狗咬人
      def bite(dog_obj,person_obj):
      print(f"{dog_obj['name']}的攻击力:{dog_obj['attack_val']}")
      print(f"{person_obj['name']}的血量:{person_obj['life_val']} ")
      person_obj['life_val'] -= dog_obj['attack_val']
      print(f"{dog_obj['name']}咬了{person_obj['name']},人掉了{dog_obj['attack_val']}血量,还剩下{person_obj['life_val']}")

      bite(dog1,P1)
      1
      2
      3
      4
      5
      6
      7
      8
      9
      #打狗
      def fight_dog(person_obj,dog_obj):
      print(f"{person_obj['name']}的攻击力:{person_obj['attack_val']}")
      print(f"{dog_obj['name']}的血量:{dog_obj['life_val']} ")
      dog_obj['life_val'] -= person_obj['attack_val']
      print(f"{person_obj['name']}打了{dog_obj['name']},狗子没有了{person_obj['attack_val']}血量,还剩下{dog_obj['life_val']}")
      print()

      fight_dog(P1,dog1)
    • 思考

      1
      2
      3
      4
      上文的打狗函数是bite(dog_obj,person_obj),我们在实际传参的时候写成bite(person_obj,dog_obj)可以吗?
      从现实的角度来说,肯定是不可以的。因为bite()函数的狗咬人,理应传入狗的伤害值和人的生命值。
      但是从程序的角度来说,这样运行也没什么问题,因为计算机不知道你是人是狗。
      那么怎么让计算机知道你是人是狗呢?这就是这篇文章探讨的主要内容了!

类的语法

语法格式: class 类的名称():

1
2
3
4
5
6
7
8
9
class dog():  #创建一个狗类
d_type = "藏獒"

def say_hi(self):
print(f"各位你们好,我的品种是{self.d_type}")

dog1 = dog() #类的一个实例化
print(dog1.d_type) #调用类的属性,属性一个静态的
dog1.say_hi() #该实列调用方法,方法是一个动作

类的初始化

1
2
3
4
5
6
7
8
9
10
11
12
class dog():  #创建一个狗类

def __init__(self,name,d_type): #初始化方法,构造方法,实例化时会自动执行,完成一些初始化操作
self.name = name
self.d_type = d_type
self.life_val = 100
def say_hi(self):
print(f"各位你们好,我的品种是{self.d_type}")

dog1 = dog("小红","藏獒") #类的一个实例化
print(dog1.d_type) #调用类的属性,属性一个静态的
dog1.say_hi() #该实列调用方法,方法是一个动作

类属性的应用场景

类属性:类变量,也就是公共属性,所有实列共享

实例属性:实例变量,成员变量,每个实例独享

1
2
3
#类属性和实例调用类属性,若数值也是一样,内存空间是一样的
print(id(dog1.life_val))
print(id(dog2.life_val))

dog.life_val变量是一样,都是100,所以可以单独设置

1
2
3
4
5
6
7
8
9
class dog():  #创建一个狗类
life_val = 100

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

def say_hi(self):
print(f"各位你们好,我的品种是{self.d_type}")

实列属性对自己不满意也可以改属性

1
2
3
#dog.life_val = 100
dog2.life_val = 200
print(id(dog2.life_val))

类之间的关系

  1. 依赖关系
  2. 关联关系
  3. 组合关系
  4. 聚合关系
  5. 继承关系,类的三大特性之一

访问限制

访问保护

在类的应用场景我们说到实例属性对自己不满意也可以改属性。

如果要让内部属性不被外部访问,可以在属性前面加上一些东西,如__

需要注意:变量名__xxx__的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用__name____score__这样的变量名。

1
2
3
4
5
6
7
8
class dog():  #创建一个狗类


def __init__(self,name,d_type):
self.__name = name
self.__d_type = d_type
self.life_val = 100
#这样的话dog2.val = 200就不能成功。因为无法访问

如果说我们有时候真的需要修改life_val怎么办?可以给dog增加一个set_life_val方法:

1
2
3
4
5
6
7
8
9
10
class dog():  #创建一个狗类
# life_val = 100
def __init__(self,name,d_type):
self.__name = name
self.__d_type = d_type
self.__life_val = 100

def set_life_val(self,life):
self.__life_val = life

除了修改,我们还需要访问life_val。但是现在显然是不能访问的,解决方案如下:

1
2
def set_life_val(self):
return self.__life_val

继承

当我们新定义一个class,可以从现有的class继承,新的这个class也就是子类。

比如我们定义一个二哈继承狗类。

1
2
class erha(dog):
pass

这样的话,二哈拥有父类dog的全部功能。

获取对象信息

使用type()

使用type()可以判断类型

1
2
type(12345)
type(dog1)

使用isinstance()

对于class的继承关系。使用type()不方便。

1
2
>>> isinstance(erha1,erha)
True

因为erha是从dog继承下来的,所以erha1也是属于dog

1
2
>>> isinstance(erha1,dog)
True

使用dir()

获取一个对象的所有属性和方法,它返回一个包含字符串的list。

1
2
3
>>>dir(123)
['__abs__', '__add__',....... 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']