类与对象

1. 类与对象

“类”和“对象”的关系:【类】是【对象】的模板。
在这里插入图片描述
在这里插入图片描述

2.类的实例化

让我们先明确一下实例化的基本格式。要注意,当类需要被实例化后再使用时,和直接使用类的格式是不同的。
直接使用类的格式如下:
在这里插入图片描述
实例化后再使用的格式如下:
在这里插入图片描述
通过对比可以看到,实例化后再使用的格式,①是空着的,意思是这里不再需要@classmethod的声明,并且在第②处,把cls替换成了self。同时,实例化后再使用的格式,需要先赋值然后再调用(第③处):
在这里插入图片描述
在第④步骤,我们需要用实例名 = 类()的方式(实例名其实就是任取一个变量名),为类创建一个实例,然后再使用实例名.函数()的方式调用对应的方法。

# 直接使用类
class 成绩单():
    @classmethod
    def 录入成绩单(cls):
        cls.学生姓名 = input('请输入学生姓名:')
        cls.语文_成绩 = int(input('请输入语文成绩:'))
        cls.数学_成绩 = int(input('请输入数学成绩:'))

    @classmethod
    def 打印成绩单(cls):
        print(cls.学生姓名 + '的成绩单如下:')
        print('语文成绩:'+ str(cls.语文_成绩))
        print('数学成绩:'+ str(cls.数学_成绩))

成绩单.录入成绩单()
成绩单.打印成绩单()

# 实例化之后
class 成绩单():   # ①不用再写@classmethod
    def 录入成绩单(self):  # ②cls变成self
        self.学生姓名 = input('请输入学生姓名:')  # ③cls.变成self.
        self.语文_成绩 = int(input('请输入语文成绩:'))
        self.数学_成绩 = int(input('请输入数学成绩:'))

    def 打印成绩单(self):
        print(self.学生姓名 + '的成绩单如下:')
        print('语文成绩:'+ str(self.语文_成绩))
        print('数学成绩:'+ str(self.数学_成绩))


成绩单1 = 成绩单() # ④创建实例对象:成绩单1

成绩单1.录入成绩单() # ⑤实例化后使用
成绩单1.打印成绩单()
>>>
请输入学生姓名:li
请输入语文成绩:93
请输入数学成绩:100
li的成绩单如下:
语文成绩:93
数学成绩:100
请输入学生姓名:do
请输入语文成绩:96
请输入数学成绩:99
do的成绩单如下:
语文成绩:96
数学成绩:99

代码实例化练习:

#与直接使用类的区别标注如下:
class 智能机器人():
  胸围 = 33
  腰围 = 44
  臀围 = 55
  智商 = 200
                                   #c@classmethod不要写
  def 自报三维(self):              #cls换为self
    print('主人,我的三围是:')
    print('胸围:'+str(self.胸围)) #cls换为self
    print('腰围:'+str(self.腰围)) #cls换为self
    print('臀围:'+str(self.臀围)) #cls换为self
    print('哈哈哈哈哈,下面粗上面细,我长得像个圆锥。')

a = 智能机器人()
a.自报三维()
>>>
主人,我的三围是:
胸围:33
腰围:44
臀围:55
哈哈哈哈哈,下面粗上面细,我长得像个圆锥。

**cls代表“类”的意思,self代表“实例”的意思,这样写是编码规范(程序员们的共识),但不是强制要求。理论上只要写个变量名占位,写什么都行,比如把self写成bbb:**不过为了不做“非主流”,我们还是乖乖地用self就好了。也就是说,当类支持实例化的时候,self是所有类方法位于首位、默认的特殊参数。换而言之,实例化后,只要你在类中用了def语句,那么就必须在其后的括号里把第一个位置留给self。另外,当类支持实例化的时候,就不能再直接使用类方法了,如果运行以下代码将会报错。不过没关系。通常来说,我们都是把类实例化后再调用。(哪怕取一个和类名相同的实例名称也可以)

class 智能机器人():
  胸围 = 33
  腰围 = 44
  臀围 = 55
  智商 = 200

  def 自报三维(self):
    print('主人,我的三围是:')
    print('胸围:'+str(self.胸围))
    print('腰围:'+str(self.腰围))
    print('臀围:'+str(self.臀围))
    print('哈哈哈哈哈,下面粗上面细,我长得像个圆锥。')

智能机器人.自报三维() #实例化后就不能直接使用类了,因为格式都不同了嘛
TypeError    Traceback (most recent call last)
<ipython-input-16-69257bd101f9> in <module>()
     13 
     14 
---> 15 智能机器人.自报三维()

TypeError: 自报三维() missing 1 required positional argument: 'self

当我们完成实例化后,对应于一个实例的属性和方法,叫“实例属性、实例方法”,不再称为“类属性、类方法”:
在这里插入图片描述

3. 实例属性和类属性

在这里插入图片描述

class():
  变量 = 100

实例1 =() # 实例化
实例2 =() # 实例化

print(.变量)
print(实例1.变量)  
print(实例2.变量)
>>>
100
100
100

我们可以修改类属性,这会导致所有实例属性变化(因为类是模板)。
在这里插入图片描述

class():
  变量 = 100

实例1 =() # 实例化
实例2 =() # 实例化

print(.变量)
print(实例1.变量)  
print(实例2.变量).变量 = 'abc'
print(实例1.变量)  
print(实例2.变量)
>>>
100
100
100
abc
abc

我们也可以修改实例属性,但这不会影响到其他实例,也不会影响到类。因为每个实例都是独立的个体。
在这里插入图片描述

class():
  变量 = 100

实例1 =() # 实例化
实例2 =() # 实例化

print('原先的类属性:')
print(.变量)
print('原先的实例1属性:')
print(实例1.变量)
print('原先的实例2属性:')
print(实例2.变量)

实例1.变量 = 'abc'
print('--------修改实例1的属性后----------')
print(实例1.变量)
print('--------修改实例2的属性后----------') 
print(实例2.变量)
>>>
原先的类属性:
100
原先的实例1属性:
100
原先的实例2属性:
100
--------修改实例1的属性后----------
abc
--------修改实例2的属性后----------
100

新增也是一样的道理,在类中新增属性会影响到实例,但在实例中新增属性只影响这个实例自己。
体验一下新增类属性:

class():
  变量1 = 100

实例 =() # 实例化.变量2 = 'abc' # 新增类属性

print(实例.变量1)
print(实例.变量2)
>>>
100
abc

体验一下新增实例属性:(将会报错,因为增加实例属性不会影响到类。)

class():
  变量1 = 100

实例 =() # 实例化

实例.变量2 = 'abc' # 新增实例属性

print(.变量2)
>>>
AttributeError                            Traceback (most recent call last)
<ipython-input-9-80c5f4dec8eb> in <module>()
      6 实例.变量2 = 'abc' # 新增实例属性
      7 
----> 8 print(.变量2)

AttributeError: type object '类' has no attribute '变量2'

4. 实例方法和类方法

和类属性一样,我们可以重写类方法,这会导致所有实例方法自动被重写。
在这里插入图片描述
“重写类方法”分成两个步骤:第一个步骤是在类的外部写一个函数,第二个步骤是把这个新函数的名字赋值给类.原始函数:
在这里插入图片描述
要注意的是,这里的赋值是在替换方法,并不是调用函数,所以【不要加上括号】—— 写成类.原始函数() = 新函数()是错误的。

class():
  def 原始函数(self):
    print('我是原始函数!')

def 新函数(self):
  print('我是重写后的新函数!')

a =()  # 实例化
a.原始函数()

# 用新函数代替原始函数,也就是【重写类方法】.原始函数 = 新函数

# 现在原始函数已经被替换了
a.原始函数()
>>>
我是原始函数!
我是重写后的新函数!

可以看到,类方法已经被替换成了新函数,所以第二次调用a.原始函数()的效果是新函数效果。

class 幸运():
  def 好运翻倍(self):
    print('好的,我把它存了起来,然后翻了888倍还给你:' + str(self.幸运数*888))

def 好运新函数(self):
  print('我是重写后的新函数!')
  print('好的,我把它存了起来,然后翻了666倍还给你:' + str(self.幸运数*666))

幸运.幸运数 = int(input('你的幸运数是多少?请输入一个整数。'))

幸运.好运翻倍 = 好运新函数
实例 = 幸运()  # 实例化
实例.好运翻倍()
>>>
你的幸运数是多少?请输入一个整数。1
我是重写后的新函数!
好的,我把它存了起来,然后翻了666倍还给你:666

我们可以通过重写类方法,让实例方法发生变化,但我们不能重写实例方法,模板给的技能不是说换就能换的。
在这里插入图片描述
如果尝试重写实例方法将会报错:

class 幸运():
  def 好运翻倍(self):
    print('好的,我把它存了起来,然后翻了888倍还给你:' + str(self.幸运数*888))

def 新好运翻倍(self):
  print('我是重写后的新函数!')
  print('好的,我把它存了起来,然后翻了666倍还给你:' + str(self.幸运数*666))

幸运.幸运数 = int(input('你的幸运数是多少?请输入一个整数。'))

实例 = 幸运()  # 实例化
实例.好运翻倍 = 新好运翻倍    # 尝试重写实例方法,将会报错
实例.好运翻倍()
>>>
你的幸运数是多少?请输入一个整数。1
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-13-366f27185690> in <module>()
     11 实例 = 幸运()  # 实例化
     12 实例.好运翻倍 = 新好运翻倍    # 尝试重写实例方法,将会报错
---> 13 实例.好运翻倍()

TypeError: 新好运翻倍() missing 1 required positional argument: 'self'

5. 小总结

在这里插入图片描述

6. 初始化函数

class():
  def __init__(self):
    print('实例化成功!')

实例 =()
>>>
实例化成功!

初始化函数的意思是,当你创建一个实例的时候,这个函数就会被调用。上面的代码在执行 实例 = 类() 的语句时,就自动调用了__init__(self)函数。初始化函数的写法是固定的格式:中间是“init”,这个单词的中文意思是“初始化”,然后前后都要有【两个下划线】,然后__init__()的括号中,第一个参数一定要写上self,不然会报错。同时,这个初始化函数照样可以传递参数,我们来看个例子。
原来函数需要这么写:

class 成绩单():
    def 录入成绩单(self):
        self.学生姓名 = input('请输入学生姓名:')
        self.语文_成绩 = int(input('请输入语文成绩:'))
        self.数学_成绩 = int(input('请输入数学成绩:'))

    def 打印成绩单(self):
        print(cls.学生姓名 + '的成绩单如下:')
        print('语文成绩:'+ str(self.语文_成绩))
        print('数学成绩:'+ str(self.数学_成绩))
        
成绩单1 = 成绩单() # 实例化
成绩单2 = 成绩单() # 实例化
成绩单3 = 成绩单() # 实例化

成绩单1.录入成绩单()
成绩单2.录入成绩单()
成绩单3.录入成绩单()

成绩单1.打印成绩单()
成绩单2.打印成绩单()
成绩单3.打印成绩单()

有了初始化函数之后就方便多了:

class 成绩单():
    def __init__(self,学生姓名,语文_成绩,数学_成绩):
        self.学生姓名 = 学生姓名
        self.语文_成绩 = 语文_成绩
        self.数学_成绩 = 数学_成绩

    def 打印成绩单(self):
        print(self.学生姓名 + '的成绩单如下:')
        print('语文成绩:'+ str(self.语文_成绩))
        print('数学成绩:'+ str(self.数学_成绩))


成绩单1 = 成绩单('张三',99,88)
成绩单2 = 成绩单('李四',64,73)
成绩单3 = 成绩单('王五',33,22)

成绩单1.打印成绩单()
成绩单2.打印成绩单()
成绩单3.打印成绩单()
>>>
张三的成绩单如下:
语文成绩:99
数学成绩:88
李四的成绩单如下:
语文成绩:64
数学成绩:73
王五的成绩单如下:
语文成绩:33
数学成绩:22

有了初始化函数后,我们可以直接把需要录入的信息作为参数传递给成绩单1、成绩单2、成绩单3这三个实例对象。这里的代码利用初始化函数 def _init _ (self, 学生姓名 , 语文_成绩, 数学_成绩),为每个实例自动创建了实例属性 self.学生姓名、self.语文_成绩、self.数学_成绩。

class 乘法表():
  def __init__(self,j):
    self.j = j    #这里需要赋值给下面参数使用,因此需要用 self.j=j

  def 打印(self):
    for i in range(1,self.j+1):
      for x in range(1,i+1):
        print( '%d X %d = %d' % (i ,x ,i*x) ,end = '  ' )
      print('  ')

print('---------代码运行的效果【1】--------')
三三乘法表 = 乘法表(3)
三三乘法表.打印()

print('---------代码运行的效果【2】--------')
五五乘法表 = 乘法表(5)
五五乘法表.打印()
>>>
---------代码运行的效果【1--------
1 X 1 = 1    
2 X 1 = 2  2 X 2 = 4    
3 X 1 = 3  3 X 2 = 6  3 X 3 = 9    
---------代码运行的效果【2--------
1 X 1 = 1    
2 X 1 = 2  2 X 2 = 4    
3 X 1 = 3  3 X 2 = 6  3 X 3 = 9    
4 X 1 = 4  4 X 2 = 8  4 X 3 = 12  4 X 4 = 16    
5 X 1 = 5  5 X 2 = 10  5 X 3 = 15  5 X 4 = 20  5 X 5 = 25    

这个案例,我们通过初始化函数传递参数,从而控制了三三乘法表、五五乘法表这些实例的打印行数,这里的数据流转图如下:在这里插入图片描述

7. 继承

类的继承很大程度也是为了避免重复性劳动。比如说当我们要写一个新的类,如果新的类有许多代码都和旧类相同,又有一部分不同的时候,就可以用“继承”的方式避免重复写代码。
在这里插入图片描述

class 父类():
  def __init__(self,参数):
    self.变量 = 参数

  def 打印属性(self):
    print('变量的值是:')
    print(self.变量)

class 子类(父类):
  pass  # pass语句代表“什么都不做”

子类实例 = 子类(2)
子类实例.打印属性()
>>>
变量的值是:
2

在这里插入图片描述

class 基础机器人():
  def __init__(self,参数):
    self.姓名 = 参数

  def 自报姓名(self):
    print('我是' + self.姓名 + '!')     

  def 卖萌(self):
    print('主人,求抱抱!')

class 高级机器人(基础机器人):
  def 高级卖萌(self):
    print('主人,每次想到怎么欺负你的时候,就感觉自己全身biubiubiu散发着智慧的光芒!')

安迪 = 高级机器人('安迪')

安迪.自报姓名()
安迪.卖萌()
安迪.高级卖萌()
>>>
我是安迪!
主人,求抱抱!
主人,每次想到怎么欺负你的时候,就感觉自己全身biubiubiu散发着智慧的光芒!

子类除了可以定制新的类方法,还能直接覆盖父类的方法,只要使用相同的类方法名称就能做到这一点。

class 基础机器人():
    def __init__(self,参数):
      self.姓名 = 参数

    def 自报姓名(self):
      print('我是' + self.姓名 + '!')     

    def 卖萌(self):
      print('主人,求抱抱!')

class 高级机器人(基础机器人):
    def 卖萌(self):  # 这里使用了相同的类方法名称“卖萌”,这样可以让子类方法覆盖父类方法
      print('主人,每次想到怎么欺负你的时候,就感觉自己全身biubiubiu散发着智慧的光芒!')

鲁宾 = 高级机器人('鲁宾')
鲁宾.自报姓名()
鲁宾.卖萌()
>>>
我是鲁宾!
主人,每次想到怎么欺负你的时候,就感觉自己全身biubiubiu散发着智慧的光芒!

我们能在子类中重写覆盖任意父类方法,哪怕是初始化函数__init__。我们再做个重写初始化函数__init__的练习:

class 基础机器人():
    def __init__(self,参数):
      self.姓名 = 参数

    def 自报姓名(self):
      print('我是' + self.姓名 + '!')     

    def 卖萌(self):
      print('主人,求抱抱!')

class 高级机器人(基础机器人):
    def __init__(self,参数1,参数2):
      self.姓名 = 参数1
      self.智商 = 参数2
      
    def 自报姓名(self):
      print('我是高级机器人' + self.姓名 + '!智商高达'+str(self.智商)+'!')

    def 高级卖萌(self):
      print('主人,每次想到怎么欺负你的时候,就感觉自己全身biubiubiu散发着智慧的光芒!')


安迪 = 高级机器人('安迪',150)

安迪.自报姓名()
安迪.高级卖萌()
>>>
我是高级机器人安迪!智商高达150!
主人,每次想到怎么欺负你的时候,就感觉自己全身biubiubiu散发着智慧的光芒!

像这样,子类从【一个父类】继承类方法,我们叫做“单继承”。还有一种更复杂的继承情况,叫“多重继承”。
在这里插入图片描述
顾名思义,“多重继承”就是一个子类从【多个父类】中继承类方法。格式是class 子类(父类1,父类2,……)。

class 基础机器人():
  def 卖萌(self):
    print('主人,求抱抱!')

 # 注:因为多重继承要求父类是平等的关系,所以这里的“高级机器人”没有继承“基础机器人”
class 高级机器人(): 
  def 高级卖萌(self): 
    print('主人,每次想到怎么欺负你的时候,就感觉自己全身biubiubiu散发着智慧的光芒!')

class 超级机器人(基础机器人,高级机器人):
  def 超级卖萌(self): 
    print('pika, qiu!')

pika = 超级机器人()
pika.卖萌()
pika.高级卖萌()
pika.超级卖萌()
>>>
主人,求抱抱!
主人,每次想到怎么欺负你的时候,就感觉自己全身biubiubiu散发着智慧的光芒!
pika, qiu!

不过,多重继承有利有弊。过度使用继承容易把事情搞复杂,就像一个人有很多爸爸必定会带来诸多麻烦。如果不是开发大型项目,不太需要用到太复杂的继承关系。

8. 课堂实操

class 出租车():
  def __init__(self,p):  #这里的init两变是两个下划线,__init__,一个会出错
      self.price = p

  def 计费(self):
    公里数 = float(input('请输入行程公里数:'))
    费用 = 公里数 * self.price
    print('费用一共是:' + str(费用) + '元')

小王的出租车 = 出租车(2.5)
小王的出租车.计费()

小李的出租车 = 出租车(3)
小李的出租车.计费()
>>>
请输入行程公里数:4
费用一共是:10.0元
请输入行程公里数:3
费用一共是:9.0

升级版本;

class 出租车():
    def __init__(self,参数1,参数2,参数3):
      self.每公里费用 = 参数1
      self.最低公里 = 参数2
      self.最低费用 = 参数3
        
    def 记录行程(self):
        self.公里数 = float(input('请输入行程公里数:'))

    def 统计费用(self):
        if self.公里数 <= self.最低公里:
          self.费用 = self.最低费用
        else:
          self.费用 = self.最低费用 + (self.公里数-self.最低公里)*self.每公里费用

    def 结算信息(self):
        print('费用一共是:' + str(self.费用) + '元')

    def 计费(self):
        self.记录行程()
        self.统计费用()
        self.结算信息()
class 电动车(出租车):     #类的继承
  def 结算信息(self):
        print('费用一共是:' + str(self.费用*0.8) + '元')

小王的出租车 = 出租车(2.5,3,15)
小王的出租车.计费()

小李的电动车 = 电动车(2.5,3,15)
小李的电动车.计费()
>>>
请输入行程公里数:10
费用一共是:32.5元
请输入行程公里数:10
费用一共是:26.0
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值