问答题
1. 类和对象是什么关系呢?
答:类和对象的关系就如同模具和用这个模具制作出的物品之间的关系。一个类为它的全部对象给出了一个统一的定义,而他的每个对象则是符合这种定义的一个实体,因此类和对象的关系就是抽象和具体的关系。
3. 类的定义有些时候或许不那么“拟物”,有时候会抽象一些,例如我们定义一个矩形类,那你会为此添加哪些属性和方法呢?
答:属性可以是长和宽,方法可以是计算周长、面积等。
4*. 类的属性定义应该尽可能抽象还是尽可能具体?
答:正确的做法是应该尽可能的抽象,因为这样更符合面向对象的思维。
5*. 请用一句话概括面向对象的几个特征?
答:
特性 | 描述 |
---|---|
封装 | 对外部隐藏对象的工作细节 |
继承 | 子类自动共享父类之间数据和方法的机制 |
多态 | 可以对不同类的对象调用相同的方法,产生不同的结果 |
6. 函数和方法有什么区别?
答:方法跟函数其实几乎完全一样,但有一点区别是方法默认有一个 self 参数。
0. 以下代码体现了面向对象编程的什么特征?
>>> "FishC.com".count('o')
1
>>> [1, 1, 2, 3, 5, 8].count(1)
2
>>> (0, 2, 4, 8, 12, 18).count(1)
0
答:体现了面向对象编程的多态特征。
2. self 参数的作用是什么?
答:绑定方法,据说有了这个参数,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__方法会在类实例化时被自动调用,我们称之为魔法方法。你可以重写这个方法,为对象定制初始化方案。
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'
>>>
答:首先你要明白类、类对象、实例对象是三个不同的名词。
我们常说的类指的是类定义,由于“Python无处不对象”,所以当类定义完之后,自然就是类对象。在这个时候,你可以对类的属性(变量)进行直接访问(MyClass.name)。
一个类可以实例化出无数的对象(实例对象),Python 为了区分是哪个实例对象调用了方法,于是要求方法必须绑定(通过 self 参数)才能调用。而未实例化的类对象直接调用方法,因为缺少 self 参数,所以就会报错。
动动手
0. 按照以下提示尝试定义一个 Person 类并生成类实例对象。
属性:姓名(默认姓名为“小甲鱼”)
方法:打印姓名
提示:方法中对属性的引用形式需加上 self,如 self.name
答:代码如下:
class Person:
def __init__(self):
self.name="小甲鱼"
def print_name(self):
print(self.name)
person=Person()
person.print_name()
1. 按照以下提示尝试定义一个矩形类并生成类实例对象。
属性:长和宽
方法:设置长和宽 -> setRect(self),获得长和宽 -> getRect(self),获得面积 -> getArea(self)
提示:方法中对属性的引用形式需加上 self,如 self.width
程序截图:
答:代码如下:
class Rectangle:
def __init__(self):
self.length=5.00
self.width=4.00
def getRect(self):
print(f"这个矩形的长是:{self.length},宽是:{self.width}")
def setRect(self):
print("请输入矩形的长和宽...")
self.length=float(input("长:"))
self.width=float(input("宽:"))
def getArea(self):
print(self.length*self.width)
rect=Rectangle()
rect.getRect()
rect.setRect()
rect.getRect()
rect.getArea()
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*. 游戏编程:按以下要求定义一个乌龟类和鱼类并尝试编写游戏。(初学者不一定可以完整实现,但请务必先自己动手,你会从中学习到很多知识的)
- 假设游戏场景为范围(x, y)为0<=x<=10,0<=y<=10
- 游戏生成1只乌龟和10条鱼
- 它们的移动方向均随机
- 乌龟的最大移动能力是2(Ta可以随机选择1还是2移动),鱼儿的最大移动能力是1
- 当移动到场景边缘,自动向反方向移动
- 乌龟初始化体力为100(上限)
- 乌龟每移动一次,体力消耗1
- 当乌龟和鱼坐标重叠,乌龟吃掉鱼,乌龟体力增加20
- 鱼暂不计算体力
- 当乌龟体力值为0(挂掉)或者鱼儿的数量为0游戏结束
答:代码如下:
import random as r #引入模块random,使用as指定别名r
legal_x=[0,10]
legal_y=[0,10] #设置游戏场景范围
class Turtle:
def __init__(self):
self.power=100 #体力上限100
#self.movement=0
self.x=r.randint(legal_x[0],legal_x[1])
self.y=r.randint(legal_y[0],legal_y[1]) #随机生成乌龟的初始坐标
def move(self):
#self.movement= int(input("请输入移动1还是2:"))
#随机计算方向并移动到新位置(x,y)
new_x=self.x+r.choice([1,2,-1,-2])
new_y=self.y+r.choice([1,2,-1,-2])
# 检查移动后是否超过场景x轴边界
if new_x<legal_x[0]:
self.x=legal_x[0]-(new_x-legal_x[0])
elif new_x>legal_x[1]:
self.x=legal_x[1]-(new_x-legal_x[1])
else:
self.x=new_x
# 检查移动后是否超过场景y轴边界
if new_y<legal_y[0]:
self.y=legal_y[0]-(new_y-legal_y[0])
elif new_y>legal_y[1]:
self.y=legal_y[1]-(new_y-legal_y[1])
else:
self.y=new_y
#体力消耗1
self.power-=1
#返回移动后的新位置
return (self.x,self.y)
def eatfish(self):
print("乌龟和鱼坐标重叠,乌龟吃掉鱼!体力增加20")
self.power+=20
if self.power>100:
self.power=100
class Fish:
def __init__(self):
#随机生成鱼的初始坐标
self.x=r.randint(legal_x[0],legal_x[1])
self.y=r.randint(legal_y[0],legal_y[1])
def move(self):
#随机计算方向并移动到新位置(x,y)
new_x=self.x+r.choice([1,-1])
new_y=self.y+r.choice([1,-1])
# 检查移动后是否超过场景x轴边界
if new_x<legal_x[0]:
self.x=legal_x[0]-(new_x-legal_x[0])
elif new_x>legal_x[1]:
self.x=legal_x[1]-(new_x-legal_x[1])
else:
self.x=new_x
# 检查移动后是否超过场景y轴边界
if new_y<legal_y[0]:
self.y=legal_y[0]-(new_y-legal_y[0])
elif new_y>legal_y[1]:
self.y=legal_y[1]-(new_y-legal_y[1])
else:
self.y=new_y
#返回移动后的新位置
return (self.x,self.y)
turtle=Turtle()
fish=[]
for i in range(10):
new_fish=Fish()
fish.append(new_fish)
while True:
if not len(fish):
print("鱼儿都吃完了。游戏结束!")
break
if not turtle.power:
print("乌龟体力耗尽,游戏结束!")
break
pos=turtle.move()
# 在迭代器中删除列表元素是非常危险的,经常会出现意想不到的问题,因为迭代器是直接引用列表的数据进行引用
# 这里我们把列表拷贝给迭代器,然后对原列表进行删除操作就不会有问题了^_^
for each_fish in fish[:]:
if each_fish.move==pos:
turtle.eat
fish.remove(each_fish)
print("有一条鱼儿被吃掉了...")