我是靠谱客的博主 霸气电脑,这篇文章主要介绍Python小甲鱼学习笔记36-4036类和对象:介绍37类和对象:面向对象编程38类和对象:继承039类和对象:拾遗040类和对象:一些相关的BIF,现在分享给大家,希望可以做个参考。

36类和对象:介绍

一、介绍对象
0、对象中的属性和方法,在编程中实际是什么?
  变量和函数。

1、类和对象是什么关系呢?
  类是对象的抽象表达,对象是类的实际表现。

2、如果我们定义了一个猫类,那你能想象出由“猫”类实例化的对象有哪些?
  黑猫,白猫,黑猫警长。

3、类的定义有些时候或许不那么“拟物”,有时候会抽象一些,例如我们定义一个矩阵类,那你会为此添加哪些属性和方法呢?
  添加长度,宽度,坐标,颜色等属性,计算面积和周长等方法。

4、类的属性定义应该尽可能抽象还是尽可能具体?
  抽象,这样才叫面向对象啊。

5、请用一句话概括面向对象的几个特征?
  封装:对外部隐藏对象的工作细节
  继承:子类自动共享父类之间数据和方法的机制
  >>> class MyList(list):
    pass 占位符 无意义
  >>> list1 = MyList()
  >>> list1.append(0)
  >>> list1
  [0]
  多态:可以对不同类的对象调用相同的方法,产生不同的结果,就是不同类的同一个方法名调用和后的结果不一样。

6、函数和方法有什么区别?
  方法多了一个self参数。

二、课后题
0、按照以下提示尝试定义一个Person类并生成类实例对象。

复制代码
1
2
3
4
5
6
7
8
# 属性:姓名(默认姓名为“小甲鱼”) # 方法:打印姓名 # 提示:放法中对属性的引用形式加上self,如self.name class Person : name = '小甲鱼' def print_name(self) : print('名字是%s' % self.name)

1、按照以下提示尝试定义一个矩阵类并生成类实例对象。
属性:长和宽
方法:设置长和宽->setRect(self),获得长和宽->getRect(self),获得面积->getArea(self)
提示:方法中对属性的引用形式加上self,如self.width

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#属性:长和宽 #方法:设置长和宽->setRect(self),获得长和宽->getRect(self), #获得面积->getArea(self) #提示:方法中对属性的引用形式加上self,如self.width class Matrix: length = 5 width = 4 def setRect(self) : print('请输入矩形的长和宽') self.length = float(input('长:')) self.width = float(input('宽:')) def getRect(self) : print('这个矩形的长是:%d,宽是:%d' % (self.length,self.width)) def getArea(self) : area = self.length * self.width print(area)

37类和对象:面向对象编程

一、self
0、self参数的作用是什么?
  绑定方法。self参数类似于人的身份证,每个实例对象都有唯一的self参数。

二、类的方法
0、如果我们不希望对象的属性或方法被外部直接引用,我们可以怎么做?(私有变量)
  我们可以在属性或方法名字前边加上双下划线,这样子从外部是无法直接访问到,会显示AttributeError错误。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
>>> class Person: __name = '小甲鱼' def getName(self): return self.__name >>> p = Person() >>> p.__name Traceback (most recent call last): File "<pyshell#7>", line 1, in <module> p.__name AttributeError: 'Person' object has no attribute '__name' >>> p.getName() '小甲鱼'

因为加了“__”就变成私有元素,类外部不能直接访问,但可以通过类的方法间接访问。但其实Python只是把元素名改变了而已,可以通过“_类名__变量名”访问,即_Person__name。

1、类在实例化后哪个方法会被自动调用?
  __init__方法会在类实例化时被自动调用,称为魔法方法,又称为构造方法。

三、课后题
0、按照以下要求定义一个游乐园门票的类,并尝试计算2个成人+1个小孩平日票价。
a.平日票价100元
b.周末票价为平日的120%
c.儿童半价

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#0、按照以下要求定义一个游乐园门票的类,并尝试计算2个成人+1个小孩平日票价。 #a.平日票价100元 #b.周末票价为平日的120% #c.儿童半价 class Ticket: def __init__(self,people = 1,date = 1) : '成人1,平日1' if people == 1: if date == 1 : self.price = 100 else : self.price = 120 else : if date == 1 : self.price = 50 else : self.price = 60 def allprice(self,number = 1) : return self.price * number adults = Ticket() child = Ticket(2,1) print(adults.allprice(2) + child.allprice(1))

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

复制代码
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#1、游戏编程:按以下要求定义一个乌龟类和鱼类并尝试编写游戏。 #(初学者不一定可以完整实现,但请务必先自己动手,你会从中学习到很多知识的) #a.假设游戏场景为范围(x,y)为0<=x<=10,0<=y<=10 #b.游戏生成1只乌龟和10条鱼 #c.它们的移动方向均随机 #d.乌龟的最大移动能力是2(Ta可以随机选择1还是2移动),鱼儿的最大移动能力是1 ##e.当移动到场景边缘,自动向反方向移动 #f.乌龟初始化体力为100(上限) #g.乌龟每移动一次,体力消耗1 #h.当乌龟和鱼坐标重叠,乌龟吃掉鱼,乌龟体力增加20 #i.鱼暂不计算体力 #j.当乌龟体力值为0(挂掉)或者鱼儿的数量为0游戏结束 ''' 结构 1.设置游戏范围(x,y)为0<=x<=10,0<=y<=10 让位置为(x,y),一直在范围内 2.生成1只乌龟和10条鱼,乌龟体力值为100,且最大为100,鱼无体力 3.乌龟和鱼都移动,移动方向随机,乌龟走1或2步,体力消耗1,鱼走1步 如果移动到边界的自动反方向 4.如果乌龟和鱼坐标重叠,乌龟吃掉鱼,乌龟体力增加20 5.当乌龟体力值为0(挂掉)或者鱼儿的数量为0游戏结束 ''' import random class Turtle : def __init__(self) : self.HP = 100 #乌龟体力值为100 (self.x,self.y) = (random.randint(0,10),random.randint(0,10)) #位置随机 self.GPS = (self.x,self.y) def crawl(self) : #乌龟移动 stepcount = random.randint(1,2) if stepcount == 1 : #一次移动一步 direction = random.randint(1,4) if direction == 1 : #上 self.y += 1 self.GPS = (self.x,self.y) elif direction == 2 : #下 self.y -= 1 self.GPS = (self.x,self.y) elif direction ==3 : #左 self.x -= 1 self.GPS = (self.x,self.y) else : #右 self.x += 1 self.GPS = (self.x,self.y) else : #一次移动两步 direction = random.randint(1,4) if direction == 1 : #上 self.y += 2 self.GPS = (self.x,self.y) elif direction == 2 : #下 self.y -= 2 self.GPS = (self.x,self.y) elif direction ==3 : #左 self.x -= 2 self.GPS = (self.x,self.y) else : #右 self.x += 2 self.GPS = (self.x,self.y) #超出边界,当移动到场景边缘,自动向反方向移动 if self.x < 0 : self.x = self.x * (-1) self.GPS = (self.x,self.y) elif self.x > 10 : self.x = 10 - (self.x - 10) self.GPS = (self.x,self.y) elif self.y < 0 : self.y = self.y * (-1) self.GPS = (self.x,self.y) elif self.y > 10 : self.y = 10 - (self.y - 10) self.GPS = (self.x,self.y) def reduceHP(self) : #乌龟每移动一次,体力消耗1 self.HP -= 1 def eat(self) : #如果乌龟和鱼坐标重叠,乌龟吃掉鱼,乌龟体力增加20 self.HP += 20 if self.HP > 100 : self.HP =100 class Fish : def __init__(self) : (self.x,self.y) = (random.randint(0,10),random.randint(0,10)) #位置随机 self.GPS = (self.x,self.y) def tour(self) : #鱼移动 direction = random.randint(1,4) if direction == 1 : #上 self.y += 1 self.GPS = (self.x,self.y) elif direction == 2 : #下 self.y -= 1 self.GPS = (self.x,self.y) elif direction ==3 : #左 self.x -= 1 self.GPS = (self.x,self.y) else : #右 self.x += 1 self.GPS = (self.x,self.y) #超出边界,当移动到场景边缘,自动向反方向移动 if self.x < 0 : self.x = self.x * (-1) self.GPS = (self.x,self.y) elif self.x > 10 : self.x = 10 - (self.x - 10) self.GPS = (self.x,self.y) elif self.y < 0 : self.y = self.y * (-1) self.GPS = (self.x,self.y) elif self.y > 10 : self.y = 10 - (self.y - 10) self.GPS = (self.x,self.y) def beeated(self) : #如果乌龟和鱼坐标重叠,乌龟吃掉鱼 del self.GPS turtle1 = Turtle() fish1 = Fish() fish2 = Fish() fish3 = Fish() fish4 = Fish() fish5 = Fish() fish6 = Fish() fish7 = Fish() fish8 = Fish() fish9 = Fish() fish10 =Fish() list1 = [fish1,fish2,fish3,fish4,fish5,fish6,fish7,fish8,fish9,fish10] count = 0 print('=========================初始的游戏状况:====================') print('乌龟的位置为:'+ str(turtle1.GPS)) print('乌龟的体力为:%d' % turtle1.HP) for i in list1 : if i.GPS == turtle1.GPS : turtle1.eat() i.beeated() del i print('鱼的位置是:'+ str(i.GPS)) while turtle1.HP > 0 and len(list1) > 0 : turtle1.crawl() turtle1.reduceHP() for i in list1 : i.tour() if i.GPS == turtle1.GPS : turtle1.eat() i.beeated() list1.remove(i) count += 1 print('=======================移动%d次之后的游戏状况:=================' % count) print('乌龟的位置为:'+ str(turtle1.GPS)) print('乌龟的体力为:%d' % turtle1.HP) for i in list1 : print('鱼的位置是:'+ str(i.GPS))

38类和对象:继承

一、格式
class 类名(父类类名):

二、测试题
0. 继承机制给程序猿带来最明显的好处是?
答:
  如果一个类 A 继承自另一个类 B,就把这个 A 称为 B 的子类,把 B 称为 A 的父类、基类或超类。继承可以使得子类具有父类的各种属性和方法,而不需要再次编写相同的代码(偷懒)。
  在子类继承父类的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类的原有属性和方法,使其获得与父类不同的功能。另外,为子类追加新的属性和方法也是常见的做法。
  但是,这里覆盖的是子类实例化对象里面的方法而已,对父类的方法没有影响。

  1. 如果按以下方式重写魔法方法 init,结果会怎样?
复制代码
1
2
3
4
class MyClass: def __init__(self): return "I love FishC.com!"

答:会报错,因为 init 特殊方法不应当返回除了 None 以外的任何对象。

  1. 当子类定义了与相同名字的属性或方法时,Python 是否会自动删除父类的相关属性或方法?

答:不会删除!Python 的做法跟其他大部分面向对象编程语言一样,都是将父类属性或方法覆盖,子类对象调用的时候会调用到覆盖后的新属性或方法,但父类的仍然还在,只是子类对象“看不到”。

  1. 假设已经有鸟类的定义,现在我要定义企鹅类继承于鸟类,但我们都知道企鹅是不会飞的,我们应该如何屏蔽父类(鸟类)中飞的方法?

答:覆盖父类方法,例如将函数体内容写 pass,这样调用 fly 方法就没有任何反应了。

  1. super 函数有什么“超级”的地方?

答:super 函数超级之处在于你不需要明确给出任何基类的名字,它会自动帮您找出所有基类以及对应的方法。由于你不用给出基类的名字,这就意味着你如果需要改变了类继承关系,你只要改变 class 语句里的父类即可,而不必在大量代码中去修改所有被继承的方法。

  1. 多重继承使用不当会导致重复调用(也叫钻石继承、菱形继承)的问题,请分析以下代码在实际编程中有可能导致什么问题?
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class A(): def __init__(self): print("进入A…") print("离开A…") class B(A): def __init__(self): print("进入B…") A.__init__(self) print("离开B…") class C(A): def __init__(self): print("进入C…") A.__init__(self) print("离开C…") class D(B, C): def __init__(self): print("进入D…") B.__init__(self) C.__init__(self) print("离开D…")

答:多重继承容易导致重复调用问题,下边实例化 D 类后我们发现 A 被前后进入了两次(有童鞋说两次就两次憋,我女朋友还不止呢……)。
这有什么危害?我举个例子,假设 A 的初始化方法里有一个计数器,那这样 D 一实例化,A 的计数器就跑两次(如果遭遇多个钻石结构重叠还要更多),很明显是不符合程序设计的初衷的(程序应该可控,而不能受到继承关系影响)。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
>>> d = D() 进入D… 进入B… 进入A… 离开A… 离开B… 进入C… 进入A… 离开A… 离开C… 离开D…
  1. 如何解决上一题中出现的问题?
复制代码
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
29
class A(): def __init__(self): print("进入A…") print("离开A…") class B(A): def __init__(self): print("进入B…") super().__init__() print("离开B…") class C(A): def __init__(self): print("进入C…") super().__init__() print("离开C…") class D(B, C): def __init__(self): print("进入D…") super().__init__() print("离开D…") >>> d = D() 进入D… 进入B… 进入C… 进入A… 离开A… 离开C… 离开B… 离开D…

三、错误覆盖父类方法时的解决方案
  这是一个有错误的程序代码:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import random as r class Fish: def __init__(self): self.x = r.randint(0, 10) self.y = r.randint(0, 10) def move(self): self.x -= 1 print("我的位置是:", self.x, self.y) class Goldfish(Fish): pass class Shark(Fish): def __init__(self): self.hungry = True def eat(self): if self.hungry: print("吃货的梦想就是天天有吃的^_^") self.hungry = False else: print("太撑了,吃不下了")

但是上面的程序是存在错误的。如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> fish = Fish() >>> fish.move() 我的位置是: 2 9 >>> goldfish = Goldfish() >>> goldfish.move() 我的位置是: -1 7 >>> shark = Shark() >>> shark.eat() 吃货的梦想就是天天有吃的^_^ >>> shark.move() Traceback (most recent call last): File "<pyshell#55>", line 1, in <module> shark.move() File "C:/Users/XiangyangDai/Desktop/上课代码/38-1.py", line 7, in move self.x -= 1 AttributeError: 'Shark' object has no attribute 'x'

我们在调用shark.move()方法的时候会报错,这是为什么呢?错误信息告诉我们,Shark 对象没有 x 的属性,Goldfish 和 Shark 都是继承Fish,Fish是有x的属性的,但是Shark重写了 init(self)方法,子类重写了父类的方法,就会把父类的方法给覆盖。要解决这个问题的话,我们应该在Shark 的类里面重写 init(self)方法的时候先调用父类的__init__(self),实现这样子的继承总共有两种技术。  
  1.调用未绑定的父类方法(该方法不重要)

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import random as r class Fish: def __init__(self): self.x = r.randint(0, 10) self.y = r.randint(0, 10) def move(self): self.x -= 1 print("我的位置是:", self.x, self.y) class Goldfish(Fish): pass class Shark(Fish): def __init__(self): #调用未绑定的父类方法 Fish.__init__(self) self.hungry = True def eat(self): if self.hungry: print("吃货的梦想就是天天有吃的^_^") self.hungry = False else: print("太撑了,吃不下了")

这样就不会报错了,需要注意的是, Fish.init(self) 中的 self 是调用它的父类的方法,但是这个 self 是子类的实例对象。
  就相当于:Fish.init(shark)。实际上,在上面出错的程序代码运行之后,我们输入下面的语句可是可以的:这里就相当于重新进行了一次初始化。

复制代码
1
2
3
4
5
>>> shark = Shark() >>> Fish.__init__(shark) >>> shark.move() 我的位置是: 6 1

2.使用 super 函数(完美方法)

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import random as r class Fish: def __init__(self): self.x = r.randint(0, 10) self.y = r.randint(0, 10) def move(self): self.x -= 1 print("我的位置是:", self.x, self.y) class Goldfish(Fish): pass class Shark(Fish): def __init__(self): #使用super函数 super().__init__() self.hungry = True def eat(self): if self.hungry: print("吃货的梦想就是天天有吃的^_^") self.hungry = False else: print("太撑了,吃不下了")

super().init(),super 函数的超级之处就在于你不用给定任何父类的名字,如果继承有多重继承或者父类的名字太过复杂的时候,也不用给出父类的名字,就可以自动帮你一层一层的找出它所有父类里面对应的方法,由于你不需要给出父类的名字,也就意味着如果你要改变类的继承关系,你只需要修改 class Shark(Fish): 里面的父类的名字即可。
  
四、多重继承

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> class Base1: def foo1(self): print("我是foo1,我为Base1代言...") >>> class Base2: def foo2(self): print("我是foo2,我为Base2代言...") >>> class C(Base1, Base2): pass >>> c = C() >>> c.foo1() 我是foo1,我为Base1代言... >>> c.foo2() 我是foo2,我为Base2代言...

多重继承可以同时继承多个父类的属性和方法,但是多重继承很容易导致代码混乱,所以当你不确定你真的必须要使用多重继承的时候,请尽量避免使用。

四、课后题
0. 定义一个点(Point)类和直线(Line)类,使用 getLen 方法可以获得直线的长度。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#设点A(X1,Y1)、点B(X2,Y2),则两点构成的直线长度|AB| =根号下坐标差的平方和 #计算开根号用 math模块中的sqrt函数 #初始化两个点对象作为参数 import math class Point : def __init__(self,x=0,y=0) : self.x = x self.y = y class Line : def __init__(self,p1,p2) : self.x = p1.x - p2.x self.y = p1.y - p2.y def getlen(self): result = math.sqrt(self.x*self.x + self.y*self.y) return result

039类和对象:拾遗

一、组合
  组合的用法很简单,举例说明:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Turtle: def __init__(self, x): self.num = x class Fish: def __init__(self, x): self.num = x class Pool: def __init__(self, x, y): self.turtle = Turtle(x) self.fish = Fish(y) def print_num(self): print("水池里有乌龟 %d 只,小鱼 %d 条!" %(self.turtle.num, self.fish.num)) >>> pool = Pool(1, 10) >>> pool.print_num() 水池里有乌龟 1 只,小鱼 10 条!

所谓的组合,就是把类的实例化放到新类里面,那么它就把旧类给组合进去了,不用使用继承了,没有什么风险了。组合一般来说就是把几个没有继承关系,没有直线关系的几个类放在一起,就是组合。要实现纵向关系之间的类,就使用继承。
  Python 的特性还支持另外一种很流行的编程模式,叫做 Mix-in,叫做混入的意思,有兴趣的可以参见-> Python Mixin 编程机制。

二、类、类对象和实例对象
  类、类对象和实例对象是三个不同的物种。先看代码:

复制代码
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
29
30
>>> class C: count = 0 >>> a = C() >>> b = C() >>> c = C() >>> a.count 0 >>> b.count 0 >>> c.count 0 >>> c.count += 10 >>> c.count 10 >>> a.count 0 >>> b.count 0 >>> C.count 0 >>> C.count += 100 >>> C.count 100 >>> a.count 100 >>> b.count 100 >>> c.count 10

我们这里有一个 C 类,只有一个属性 count ,初始化为0。实例化一个 a,一个 b,一个 c,显然 a.count = 0,b.count = 0,c.count = 0。如果对 c.count += 10,现在 c.count = 10,但是 a.count = 0,b.count = 0。因为 C 是一个类,在写完 C 之后就变成了一个类对象,因为Python无处不对象,所有的东西都是对象,方法也是对象,所以我们这里 C.count = 0 也是等于 0 。此时我们对这个类对象加等于100 , C.count += 100,此时 a.count = 100,b.count = 100,但是 c.count = 10。为什么会这样呢?
  其实是因为 c.count += 10 这里 c.count 被赋值的时候,我们是对实例化对象 c 的属性进行赋值,相当于我们生成了一个 count 来覆盖类对象的 count。
  类定义到类对象,还有实例对象a,b,c,需要注意的是,类中定义的属性都是静态属性,就像 C 里面的count,类属性和类对象是相互绑定的,并不会依赖于下面的实例对象,所以当 c.count += 10 的时候,并不会影响到 C,只是改变了 c 自身,因为在 c.count += 10 的时候,是实例对象 c 多了一个count 的属性,也就是实例属性,它把类属性给覆盖了。这在以后还会继续讲解,在此之前,我们先谈一下:如果属性的名字和方法相同时,属性会把方法覆盖掉。举例说明:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> class C: def x(self): print("X-man") >>> c = C() >>> c.x() X-man >>> c.x = 1 >>> c.x 1 >>> c.x() Traceback (most recent call last): File "<pyshell#48>", line 1, in <module> c.x() TypeError: 'int' object is not callable

这就是初学者容易发生的一个问题,如果属性的名字和方法名相同,属性会覆盖方法。为了避免名字上的冲突,大家应该遵守一些约定俗成的规矩:
  1.不要试图在一个类里边定义出所有能想到的特征和方法,应该使用继承和组合机制来进行扩展。
  2.用不同词性命名,如属性名用名词,方法名用动词。
  
三、到底什么是绑定?
  Python 严格要求方法需要有实例才能被调用,这种限制其实就是Python 所谓的绑定概念。

四、测试题
0. 什么是组合(组成)?
答:Python 继承机制很有用,但容易把代码复杂化以及依赖隐含继承。因此,经常的时候,我们可以使用组合来代替。在Python里组合其实很简单,直接在类定义中把需要的类放进去实例化就可以了。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 乌龟类 class Turtle: def __init__(self, x): self.num = x // 鱼类 class Fish: def __init__(self, x): self.num = x // 水池类 class Pool: def __init__(self, x, y): self.turtle = Turtle(x) // 组合乌龟类进来 self.fish = Fish(y) // 组合鱼类进来 def print_num(self): print("水池里总共有乌龟 %d 只,小鱼 %d 条!" % (self.turtle.num, self.fish.num)) >>> pool = Pool(1, 10) >>> pool.print_num()
  1. 什么时候用组合,什么时候用继承?
    答:根据实际应用场景确定。简单的说,组合用于“有一个”的场景中,继承用于“是一个”的场景中。例如,水池里有一个乌龟,天上有一个鸟,地上有一个小甲鱼,这些适合使用组合。青瓜是瓜,女人是人,鲨鱼是鱼,这些就应该使用继承啦。

  2. 类对象是在什么时候产生?
    答:当你这个类定义完的时候,类定义就变成类对象,可以直接通过“类名.属性”或者“类名.方法名()”引用或使用相关的属性或方法。

  3. 如果对象的属性跟方法名字相同,会怎样?
    答:如果对象的属性跟方法名相同,属性会覆盖方法。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class C: def x(self): print('Xman') >>> c = C() >>> c.x() Xman >>> c.x = 1 >>> c.x 1 >>> c.x() Traceback (most recent call last): File "<pyshell#20>", line 1, in <module> c.x() TypeError: 'int' object is not callable
  1. 请问以下类定义中哪些是类属性,哪些是实例属性?
复制代码
1
2
3
4
5
6
7
class C: num = 0 def __init__(self): self.x = 4 self.y = 5 C.count = 6

答:num 和 count 是类属性(静态变量),x 和 y 是实例属性。大多数情况下,你应该考虑使用实例属性,而不是类属性(类属性通常仅用来跟踪与类相关的值)。

  1. 请问以下代码中,bb 对象为什么调用 printBB() 方法失败?
复制代码
1
2
3
4
5
6
7
8
9
10
class BB: def printBB(): print("no zuo no die") >>> bb = BB() >>> bb.printBB() Traceback (most recent call last): File "<pyshell#8>", line 1, in <module> bb.printBB() TypeError: printBB() takes 0 positional arguments but 1 was given

答:因为 Python 严格要求方法需要有实例才能被调用,这种限制其实就是 Python 所谓的绑定概念。所以 Python 会自动把 bb 对象作为第一个参数传入,所以才会出现 TypeError:“需要 0 个参数,但实际传入了 1 个参数“。
正确的做法应该是:

复制代码
1
2
3
4
5
6
7
class BB: def printBB(self): print("no zuo no die") >>> bb = BB() >>> bb.printBB() no zuo no die

五、课后题
0.

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#请动手在一个类中定义一个变量,用于跟踪该类有多少个实例被创建, #当实例化一个对象,这个变量+1,当销毁一个对象,这个变量自动减一 class Myclass: count = 0 def __init__(self) : Myclass.count += 1 def __del__(self) : Myclass.count -= 1 ''' >>> a = Myclass() >>> b = Myclass() >>> c = Myclass() >>> Myclass.count 3 >>> del a >>> Myclass.count 2 >>> del b,c >>> Myclass.count 0 '''
  1. 定义一个栈(Stack)类,用于模拟一种具有后进先出(LIFO)特性的数据结构。
复制代码
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#定义一个栈类,用于模拟一种具有后进先出的特性的数据结构。 #方法名: 含义: #isEmpty() ;判断当前栈是否为空(返回True或False) #push(); 往栈的顶部压入一个数据项 #pop() ; 从栈顶弹出一个数据项(并在栈中删除) #top() ; 显示当前栈顶的一个数据项 #bottom() ; 显示当前栈底的一个数据项 class Stack : def __init__(self, start=[]): self.list1 = [] for x in start: self.push(x) def isEmpty(self) : if len(self.list1) : return True else : return False def push(self,x) : self.list1.append(x) def pop(self) : if len(self.list1) == 0 : return '栈为空' else : self.list1.pop() def top(self) : length = len(self.list1) if length == 0 : return '栈为空' else : return self.list1[length - 1]#self.list1[-1]更简单 def bottom(self) : length = len(self.list1) if length == 0 : return '栈为空' else : return self.list1[0]

040类和对象:一些相关的BIF

一、issubclass(class, classinfo)
  如果第一个参数 class 是第二个参数 classinfo 的子类,就返回 True,关于这个函数有几点需要注意的:
  1.一个类被认为是其自身的子类
  2.classinfo 可以是类对象组成的元组,只要 class 是其中一个候选类的子类,就返回 True
  
二、isinstance(object, classinfo)
  检查一个实例对象 object 是否属于一个类 classinfo,关于这个函数有几点需要注意的:
  1.如果第一个参数不是对象,则永远返回 False
  2.如果第二个参数不是类或者由类对象组成的元组,则抛出一个 TypeError 异常
  
另外,Python 提供了几个BIF让我们访问对象的属性:

三、hasattr(object, name) attr = attribute:属性
  测试一个对象是否有指定的属性。name 要用引号把属性名引起来。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
>>> class C: def __init__(self, x = 0): self.x = x >>> c1 = C() >>> hasattr(c1, "x") True >>> hasattr(c1, x) Traceback (most recent call last): File "<pyshell#71>", line 1, in <module> hasattr(c1, x) NameError: name 'x' is not defined

四、getattr(object, name[ , default] )
  返回对象指定的属性值。如果指定的属性不存在,如果你有设置 default,它会把这个default 参数打印出来,否则会抛出一个AttributeError异常。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> class C: def __init__(self, x = 0): self.x = x >>> c1 = C() >>> getattr(c1, 'x') 0 >>> getattr(c1, 'y') Traceback (most recent call last): File "<pyshell#80>", line 1, in <module> getattr(c1, 'y') AttributeError: 'C' object has no attribute 'y' >>> getattr(c1, 'y', '你所访问的属性不存在') '你所访问的属性不存在'

五、setattr(object, name, value)
  设定对象中指定属性的值,如果指定的属性不存在,会新建一个新的属性,并给其赋值。

复制代码
1
2
3
4
>>> setattr(c1, 'y', '来自江南的你') >>> getattr(c1, 'y', '你所访问的属性不存在') '来自江南的你'

六、delattr(object, name)
  删除对象中指定的属性,如果属性不存在,就抛出一个AttributeError异常。
  
七、property(fget = None, fset = None, fdel = None, doc = None)  
  俗话说,条条大路通罗马。Python 其实提供了好几个方式供你选择,property 是一个BIF,作用是通过属性设置属性,
  property 函数的作用就是设置一个属性,这个属性就是去设置定义好的属性,它的第一个参数 fget 是获取属性的方法,第一个参数 fset 是设置属性的方法,第一个参数 fdel 是删除属性的方法。举例说明:

复制代码
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
29
30
>>> class C: def __init__(self, size = 10): self.size = size def getSize(self): return self.size def setSize(self, value): self.size = value def delSize(self): del self.size x = property(getSize, setSize, delSize) >>> c1 = C() >>> c1.x 10 >>> c1.getSize() 10 >>> c1.x = 18 >>> c1.getSize() 18 >>> c1.setSize(20) >>> c1.x 20 >>> del c1.x >>> c1.getSize() Traceback (most recent call last): File "<pyshell#104>", line 1, in <module> c1.getSize() File "<pyshell#94>", line 5, in getSize return self.size AttributeError: 'C' object has no attribute 'size'

property 的优势:举个例子,在上面这个例子中,这个程序慢慢写的很复杂了,有一天,你想把这个程序进行大改,把函数名进行改写,如果没有 property,那你提供给用户的调用接口就西药修改,就会降低用户体验,但是有了property,问题就不存在了,因为提供给用户的接口都是 x,程序里面无论如何修改,property里面的参数跟着改进行了,用户还是只用调用 x 来设置或者获取 size 属性就可以了。

八、测试题
  0. 如何判断一个类是否为另一个类的子类?
答:使用 issubclass(class, classinfo) 函数,如果第一个参数(class)是第二个参数(classinfo)的一个子类,则返回 True,否则返回 False。

1. 如何判断对象 a 是否为 类 A 的实例对象?
答:使用 isinstance(object, classinfo) 函数,如果第一个参数(object)是第二个参数(classinfo)的实例对象,则返回 True,否则返回 False。
另外以下这些常识你应该知道:
  如果 object是 classinfo 的子类的一个实例,也符合条件
  如果第一个参数不是对象,则永远返回False
  classinfo 可以是类对象组成的元祖,只要class与其中任何一个候选类的子类,则返回 True
  如果第二个参数不是类或者由类对象组成的元祖,会抛出一个 TypeError 异常
  
  2. 如何优雅地避免访问对象不存在的属性(不产生异常)?
答:有两种方法可以做到。
  第一种先使用 hasattr(object, name) 函数判断属性是否存在,如果存在再访问(第一个参数(object)是对象,第二个参数(name)是属性名的字符串形式);
  第二种方法是直接使用 getattr(object, name[, default]) 函数并设置 default 参数(返回对象指定的属性值,如果指定的属性不存在,返回default(可选参数)的值)。

3. Python 的一些 BIF 很奇怪,但却十分有用。请问 property() 函数的作用是什么?
答:property() 函数允许编程人员轻松、有效地管理属性访问。

4. 请补充以下代码,使程序可以正常运行:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class C: def __init__(self, size=10): self.size = size def getXSize(self): return self.size def setXSize(self, value): self.size = value def delXSize(self): del self.size # 此处应该补充一句代码,程序才能正常运行 >>> c.x 10 >>> c.x = 12 >>> c.x 12

答:x = property(getXSize, setXSize, delXSize)

5. 通过自学【Python扩展阅读】Python 函数修饰符(装饰器).的使用,使用修饰符修改以下代码:
代码A:

复制代码
1
2
3
4
5
6
class CodeA: def foo(): print("调用静态方法 foo()") # 将 foo() 方法设置为静态方法 foo = staticmethod(foo)

代码B:

复制代码
1
2
3
4
5
6
class CodeB: def foo(cls): print("调用类方法 foo()") # 将 foo() 方法设置为类方法 foo = classmethod(foo)

答:其实正是因为设置静态方法和类方法过于讨人吐槽,因此 Python 的作者才开发出了函数修饰符的形式替代。

代码A:

复制代码
1
2
3
4
5
class CodeA: @staticmethod def foo(): print("调用静态方法 foo()")

代码B:

复制代码
1
2
3
4
5
class CodeB: @classmethod def foo(cls): print("调用类方法 foo()")
  1. 你真的理解了修饰符的用法吗?那请你写出以下代码没有用上修饰符的等同形式:
复制代码
1
2
3
4
@something def f(): print("I love FishC.com!")

答:其实 Python 的修饰符就是一种优雅的封装,但要注意的是只可以在模块或类定义内对函数进行修饰,不允许修饰一个类。

一个修饰符就是一个函数,它将被修饰的函数做为参数,并返回修饰后的同名函数或其它可调用的东西。

复制代码
1
2
3
4
5
6
7
8
@something def f(): print("I love FishC.com!") # 相当于 def f(): print("I love FishC.com!") f = something(f)
  1. 通过自学【Python扩展阅读】property 的详细使用方法,将第 4 题的代码修改为“使用属性修饰符创建描述符”的方式实现。
    答:可能你还没听说过描述符(这个概念在你学完接下来的几节课自然会了解),但这一点都影响聪明的你修改这个程序。

代码清单:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
class C: def __init__(self, size=10): self.size = size @property def x(self): return self.size @x.setter def x(self, value): self.size = value @x.deleter def x(self): del self.size

最后

以上就是霸气电脑最近收集整理的关于Python小甲鱼学习笔记36-4036类和对象:介绍37类和对象:面向对象编程38类和对象:继承039类和对象:拾遗040类和对象:一些相关的BIF的全部内容,更多相关Python小甲鱼学习笔记36-4036类和对象:介绍37类和对象:面向对象编程38类和对象:继承039类和对象:拾遗040类和对象:一些相关内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(55)

评论列表共有 0 条评论

立即
投稿
返回
顶部