第037讲:类和对象:面向对象编程

课堂小笔记

面向对象最重要的概念就是类(Class)和实例(Instance),必须牢记类是抽象的模板,比如Student类,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同。

self是什么?

Python的self就相当于C++的this指针。类是图纸,对象是实例化的东西。由同一个类可以生成无数个对象,这些对象长得都很相似,都是来源于同一个类的属性和方法,当一个对象的方法被调用的时候,对象会将自身作为第一个参数传给这个self参数,接收到这个self的时候Python就知道你是哪一个对象在调用方法了。

为什么都是调用kick方法,为啥结果不一样。因为在调用的时候,a.kick()的这里他有第一个参数,这个参数是隐藏的,就是把a这个对象的标志传进去;那setName()这里的self就接收到了,self.name就会去找到这个a对象的name属性,然后kick把他赋值打印出来。是Python默默在工作的。

(类里面没有定义的属性都可以赋值,因为类是共有属性,对象可以有自己的私有属性)

你听说过Python的魔法方法吗?

被双下划线包围。

__init__(self),称为构造方法,魔力体现在:只要在实例化一个对象的时候,那么这个方法就会在对象被创建的时候自动调用。(传说中的构成函数?)

共有和私有

苍井空是世界的,老婆是自己的!

name mangling 名字改编,名字重整。在Python中定义私有变量只需要在变量名或函数名前加上“__”两个下划线,那么这个函数或者变量就变为私有的了。

可以通过p._Person__name

是伪私有。

课后测试题及答案

测试题:

0. 以下代码体现了面向对象编程的什么特征?

>>> "FishC.com".count('o')
1
>>> [1, 1, 2, 3, 5, 8].count(1)
2
>>> (0, 2, 4, 8, 12, 18).count(1)
0

都可以调用同一个方法,但是结果不一定相同。

 答:体现了面向对象编程的多态特征。

1. 当程序员不想把同一段代码写几次,他们发明了函数解决了这种情况。当程序员已经有了一个类,而又想建立一个非常相近的新类,他们会怎么做呢?

非常相近的新类,可以用继承吧,继承原先的类,再自己修改

答:他们会定义一个新类继承已有的这个类,这样子就只需要简单添加重写需要的方法即可。例如已有龟类,那么如果要新定义一个甲鱼类,我们只需要让甲鱼类继承已有的龟类,然后重写壳的属性为“软的”即可(据说甲鱼的壳是软的)。

2. self参数的作用是什么?

用来接收对象的,可以让Python知道是哪个对象在使用方法和属性

答:绑定方法,据说有了这个参数,Python 再也不会傻傻分不清是哪个对象在调用方法了,你可以认为方法中的 self 其实就是实例对象的唯一标志

3. 如果我们不希望对象的属性或方法被外部直接引用,我们可以怎么做?

可以使用“__”双下划线来使其私有化,虽然是伪私有,因为还是可以通过_类名__方法/属性 来引用。

答:我们可以在属性或方法名字前边加上双下划线,这样子从外部是无法直接访问到,会显示AttributeError错误。

>>> class Person:
__name = '小甲鱼'
        def getName(self):
                return self.__name

>>> p = Person()
>>> p.__name
Traceback (most recent call last):
  File "<pyshell#56>", line 1, in <module>
    p.__name
AttributeError: 'Person' object has no attribute '__name'
>>> p.getName()
'小甲鱼'

我们把getName方法称之为“访问器”。Python事实上是采用一种叫“name mangling”技术,将以双下划线开头的变量名巧妙的改了个名字而已,我们仍然可以在外部通过“_类名__变量名”的方式访问:

>>> p._Person__name'小甲鱼'

当然我们并不提倡这种抬杠较真粗暴不文明的访问形式……!

4. 类在实例化后哪个方法会被自动调用?

__init__()

答:__init__方法会在类实例化时被自动调用,我们称之为魔法方法。你可以重写这个方法,为对象定制初始化方案

5. 请解释下边代码错误的原因:

class MyClass:
        name = 'FishC'
        def myFun(self):
                print("Hello FishC!")
                
>>> MyClass.name
'FishC'
>>> MyClass.myFun()
Traceback (most recent call last):
  File "<pyshell#6>", line 1, in <module>
    MyClass.myFun()
TypeError: myFun() missing 1 required positional argument: 'self'
>>>

没有一个对象传给self参数,

答:首先你要明白类、类对象、实例对象是三个不同的名词

我们常说的类指的是类定义,由于“Python无处不对象”,所以当类定义完之后,自然就是类对象。在这个时候,你可以对类的属性(变量)进行直接访问(MyClass.name)。

一个类可以实例化出无数的对象(实例对象),Python 为了区分是哪个实例对象调用了方法,于是要求方法必须绑定(通过 self 参数)才能调用。而未实例化的类对象直接调用方法,因为缺少 self 参数,所以就会报错。

动动手

0. 按照以下要求定义一个游乐园门票的类,并尝试计算2个成人+1个小孩平日票价。

平日票价100元

周末票价为平日的120%

儿童半票

 答案:

class Ticket():
        def __init__(self, weekend=False, child=False):
                self.exp = 100
                if weekend:
                        self.inc = 1.2
                else:
                        self.inc = 1
                if child:
                        self.discount = 0.5
                else:
                        self.discount = 1
        def calcPrice(self, num):
                return self.exp * self.inc * self.discount * num

>>> adult = Ticket()
>>> child = Ticket(child=True)
>>> print("2个成人 + 1个小孩平日票价为:%.2f" % (adult.calcPrice(2) + child.calcPrice(1)))
2个成人 + 1个小孩平日票价为:250.00

1. 游戏编程:按以下要求定义一个乌龟类和鱼类并尝试编写游戏。(初学者不一定可以完整实现,但请务必先自己动手,你会从中学习到很多知识的^_^)

 1 import random as r
 2 
 3 legal_x = [0, 10]
 4 legal_y = [0, 10]
 5 
 6 class Turtle:
 7     def __init__(self):
 8         # 初始体力
 9         self.power = 100
10         # 初始位置随机
11         self.x = r.randint(legal_x[0], legal_x[1])
12         self.y = r.randint(legal_y[0], legal_y[1])
13 
14     def move(self):
15         # 随机计算方向并移动到新的位置(x, y)
16         new_x = self.x + r.choice([1, 2, -1, -2])
17         new_y = self.y + r.choice([1, 2, -1, -2])
18         # 检查移动后是否超出场景x轴边界
19         if new_x < legal_x[0]:
20             self.x = legal_x[0] - (new_x - legal_x[0])
21         elif new_x > legal_x[1]:
22             self.x = legal_x[1] - (new_x - legal_x[1])
23         else:
24             self.x = new_x
25         # 检查移动后是否超出场景y轴边界
26         if new_y < legal_y[0]:
27             self.y = legal_y[0] - (new_y - legal_y[0])
28         elif new_y > legal_y[1]:
29             self.y = legal_y[1] - (new_y - legal_y[1])
30         else:
31             self.y = new_y        
32         # 体力消耗
33         self.power -= 1
34         # 返回移动后的新位置
35         return (self.x, self.y)
36 
37     def eat(self):
38         self.power += 20
39         if self.power > 100:
40             self.power = 100
41 
42 class Fish:
43     def __init__(self):
44         self.x = r.randint(legal_x[0], legal_x[1])
45         self.y = r.randint(legal_y[0], legal_y[1])
46         
47     def move(self):
48         # 随机计算方向并移动到新的位置(x, y)
49         new_x = self.x + r.choice([1, -1])
50         new_y = self.y + r.choice([1, -1])
51         # 检查移动后是否超出场景x轴边界
52         if new_x < legal_x[0]:
53             self.x = legal_x[0] - (new_x - legal_x[0])
54         elif new_x > legal_x[1]:
55             self.x = legal_x[1] - (new_x - legal_x[1])
56         else:
57             self.x = new_x
58         # 检查移动后是否超出场景y轴边界
59         if new_y < legal_y[0]:
60             self.y = legal_y[0] - (new_y - legal_y[0])
61         elif new_y > legal_y[1]:
62             self.y = legal_y[1] - (new_y - legal_y[1])
63         else:
64             self.y = new_y
65         # 返回移动后的新位置
66         return (self.x, self.y)
67 
68 turtle = Turtle()
69 fish = []
70 for i in range(10):
71     new_fish = Fish()
72     fish.append(new_fish)
73 
74 while True:
75     if not len(fish):
76         print("鱼儿都吃完了,游戏结束!")
77         break
78     if not turtle.power:
79         print("乌龟体力耗尽,挂掉了!")
80         break
81 
82     pos = turtle.move()
83     # 在迭代器中删除列表元素是非常危险的,经常会出现意想不到的问题,因为迭代器是直接引用列表的数据进行引用
84     # 这里我们把列表拷贝给迭代器,然后对原列表进行删除操作就不会有问题了^_^
85     for each_fish in fish[:]:
86         if each_fish.move() == pos:
87             # 鱼儿被吃掉了
88             turtle.eat()
89             fish.remove(each_fish)
90             print("有一条鱼儿被吃掉了...")

作者:Agiroy_70

本博客所有文章仅用于学习、研究和交流目的,欢迎非商业性质转载。

博主的文章主要是记录一些学习笔记、作业等。文章来源也已表明,由于博主的水平不高,不足和错误之处在所难免,希望大家能够批评指出。

博主是利用读书、参考、引用、抄袭、复制和粘贴等多种方式打造成自己的文章,请原谅博主成为一个无耻的文档搬运工!

原文地址:https://www.cnblogs.com/hale547/p/13335080.html