一、介绍对象
0、对象中的属性和方法,在编程中实际是什么?
变量和函数。
1、类和对象是什么关系呢?
类是对象的抽象表达,对象是类的实际表现。
2、如果我们定义了一个猫类,那你能想象出由“猫”类实例化的对象有哪些?
黑猫,白猫,黑猫警长。
3、类的定义有些时候或许不那么“拟物”,有时候会抽象一些,例如我们定义一个矩阵类,那你会为此添加哪些属性和方法呢?
添加长度,宽度,坐标,颜色等属性,计算面积和周长等方法。
4、类的属性定义应该尽可能抽象还是尽可能具体?
抽象,这样才叫面向对象啊。
5、请用一句话概括面向对象的几个特征?
封装:对外部隐藏对象的工作细节
继承:子类自动共享父类之间数据和方法的机制
>>> class MyList(list):
pass 占位符 无意义
>>> list1 = MyList()
>>> list1.append(0)
>>> list1
[0]
多态:可以对不同类的对象调用相同的方法,产生不同的结果,就是不同类的同一个方法名调用和后的结果不一样。
6、函数和方法有什么区别?
方法多了一个self参数。
二、课后题
0、按照以下提示尝试定义一个Person类并生成类实例对象。
# 属性:姓名(默认姓名为“小甲鱼”)
# 方法:打印姓名
# 提示:放法中对属性的引用形式加上self,如self.name
class Person :
name = '小甲鱼'
def print_name(self) :
print('名字是%s' % self.name)
1、按照以下提示尝试定义一个矩阵类并生成类实例对象。
属性:长和宽
方法:设置长和宽->setRect(self),获得长和宽->getRect(self),获得面积->getArea(self)
提示:方法中对属性的引用形式加上self,如self.width
#属性:长和宽
#方法:设置长和宽->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)
一、self
0、self参数的作用是什么?
绑定方法。self参数类似于人的身份证,每个实例对象都有唯一的self参数。
二、类的方法
0、如果我们不希望对象的属性或方法被外部直接引用,我们可以怎么做?(私有变量)
我们可以在属性或方法名字前边加上双下划线,这样子从外部是无法直接访问到,会显示AttributeError错误。
>>> class Person:
__name = '小甲鱼'
def getName(self):
return self.__name
>>> p = Person()
>>> p.__name
Traceback (most recent call last):
File "", line 1, in
p.__name
AttributeError: 'Person' object has no attribute '__name'
>>> p.getName()
'小甲鱼'
因为加了“__”就变成私有元素,类外部不能直接访问,但可以通过类的方法间接访问。但其实Python只是把元素名改变了而已,可以通过“_类名__变量名”访问,即_Person__name。
1、类在实例化后哪个方法会被自动调用?
__init__方法会在类实例化时被自动调用,称为魔法方法,又称为构造方法。
三、课后题
0、按照以下要求定义一个游乐园门票的类,并尝试计算2个成人+1个小孩平日票价。
a.平日票价100元
b.周末票价为平日的120%
c.儿童半价
#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、游戏编程:按以下要求定义一个乌龟类和鱼类并尝试编写游戏。
#(初学者不一定可以完整实现,但请务必先自己动手,你会从中学习到很多知识的)
#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))
一、格式
class 类名(父类类名):
二、测试题
0. 继承机制给程序猿带来最明显的好处是?
答:
如果一个类 A 继承自另一个类 B,就把这个 A 称为 B 的子类,把 B 称为 A 的父类、基类或超类。继承可以使得子类具有父类的各种属性和方法,而不需要再次编写相同的代码(偷懒)。
在子类继承父类的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类的原有属性和方法,使其获得与父类不同的功能。另外,为子类追加新的属性和方法也是常见的做法。
但是,这里覆盖的是子类实例化对象里面的方法而已,对父类的方法没有影响。
class MyClass:
def __init__(self):
return "I love FishC.com!"
答:会报错,因为 init 特殊方法不应当返回除了 None 以外的任何对象。
答:不会删除!Python 的做法跟其他大部分面向对象编程语言一样,都是将父类属性或方法覆盖,子类对象调用的时候会调用到覆盖后的新属性或方法,但父类的仍然还在,只是子类对象“看不到”。
答:覆盖父类方法,例如将函数体内容写 pass,这样调用 fly 方法就没有任何反应了。
答:super 函数超级之处在于你不需要明确给出任何基类的名字,它会自动帮您找出所有基类以及对应的方法。由于你不用给出基类的名字,这就意味着你如果需要改变了类继承关系,你只要改变 class 语句里的父类即可,而不必在大量代码中去修改所有被继承的方法。
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 的计数器就跑两次(如果遭遇多个钻石结构重叠还要更多),很明显是不符合程序设计的初衷的(程序应该可控,而不能受到继承关系影响)。
>>> d = D()
进入D…
进入B…
进入A…
离开A…
离开B…
进入C…
进入A…
离开A…
离开C…
离开D…
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…
三、错误覆盖父类方法时的解决方案
这是一个有错误的程序代码:
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("太撑了,吃不下了")
但是上面的程序是存在错误的。如下:
>>> fish = Fish()
>>> fish.move()
我的位置是: 2 9
>>> goldfish = Goldfish()
>>> goldfish.move()
我的位置是: -1 7
>>> shark = Shark()
>>> shark.eat()
吃货的梦想就是天天有吃的^_^
>>> shark.move()
Traceback (most recent call last):
File "" , 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.调用未绑定的父类方法(该方法不重要)
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)。实际上,在上面出错的程序代码运行之后,我们输入下面的语句可是可以的:这里就相当于重新进行了一次初始化。
>>> shark = Shark()
>>> Fish.__init__(shark)
>>> shark.move()
我的位置是: 6 1
2.使用 super 函数(完美方法)
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): 里面的父类的名字即可。
四、多重继承
>>> 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 方法可以获得直线的长度。
#设点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
一、组合
组合的用法很简单,举例说明:
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 编程机制。
二、类、类对象和实例对象
类、类对象和实例对象是三个不同的物种。先看代码:
>>> 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 的属性,也就是实例属性,它把类属性给覆盖了。这在以后还会继续讲解,在此之前,我们先谈一下:如果属性的名字和方法相同时,属性会把方法覆盖掉。举例说明:
>>> 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 "" , line 1, in <module>
c.x()
TypeError: 'int' object is not callable
这就是初学者容易发生的一个问题,如果属性的名字和方法名相同,属性会覆盖方法。为了避免名字上的冲突,大家应该遵守一些约定俗成的规矩:
1.不要试图在一个类里边定义出所有能想到的特征和方法,应该使用继承和组合机制来进行扩展。
2.用不同词性命名,如属性名用名词,方法名用动词。
三、到底什么是绑定?
Python 严格要求方法需要有实例才能被调用,这种限制其实就是Python 所谓的绑定概念。
四、测试题
0. 什么是组合(组成)?
答:Python 继承机制很有用,但容易把代码复杂化以及依赖隐含继承。因此,经常的时候,我们可以使用组合来代替。在Python里组合其实很简单,直接在类定义中把需要的类放进去实例化就可以了。
// 乌龟类
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()
什么时候用组合,什么时候用继承?
答:根据实际应用场景确定。简单的说,组合用于“有一个”的场景中,继承用于“是一个”的场景中。例如,水池里有一个乌龟,天上有一个鸟,地上有一个小甲鱼,这些适合使用组合。青瓜是瓜,女人是人,鲨鱼是鱼,这些就应该使用继承啦。
类对象是在什么时候产生?
答:当你这个类定义完的时候,类定义就变成类对象,可以直接通过“类名.属性”或者“类名.方法名()”引用或使用相关的属性或方法。
如果对象的属性跟方法名字相同,会怎样?
答:如果对象的属性跟方法名相同,属性会覆盖方法。
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 "" , line 1, in <module>
c.x()
TypeError: 'int' object is not callable
class C:
num = 0
def __init__(self):
self.x = 4
self.y = 5
C.count = 6
答:num 和 count 是类属性(静态变量),x 和 y 是实例属性。大多数情况下,你应该考虑使用实例属性,而不是类属性(类属性通常仅用来跟踪与类相关的值)。
class BB:
def printBB():
print("no zuo no die")
>>> bb = BB()
>>> bb.printBB()
Traceback (most recent call last):
File "" , line 1, in <module>
bb.printBB()
TypeError: printBB() takes 0 positional arguments but 1 was given
答:因为 Python 严格要求方法需要有实例才能被调用,这种限制其实就是 Python 所谓的绑定概念。所以 Python 会自动把 bb 对象作为第一个参数传入,所以才会出现 TypeError:“需要 0 个参数,但实际传入了 1 个参数“。
正确的做法应该是:
class BB:
def printBB(self):
print("no zuo no die")
>>> bb = BB()
>>> bb.printBB()
no zuo no die
五、课后题
0.
#请动手在一个类中定义一个变量,用于跟踪该类有多少个实例被创建,
#当实例化一个对象,这个变量+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
'''
#定义一个栈类,用于模拟一种具有后进先出的特性的数据结构。
#方法名: 含义:
#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]
一、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 要用引号把属性名引起来。
>>> 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 "" , line 1, in <module>
hasattr(c1, x)
NameError: name 'x' is not defined
四、getattr(object, name[ , default] )
返回对象指定的属性值。如果指定的属性不存在,如果你有设置 default,它会把这个default 参数打印出来,否则会抛出一个AttributeError异常。
>>> 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 "" , line 1, in <module>
getattr(c1, 'y')
AttributeError: 'C' object has no attribute 'y'
>>> getattr(c1, 'y', '你所访问的属性不存在')
'你所访问的属性不存在'
五、setattr(object, name, value)
设定对象中指定属性的值,如果指定的属性不存在,会新建一个新的属性,并给其赋值。
>>> 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 是删除属性的方法。举例说明:
>>> 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 "" , line 1, in <module>
c1.getSize()
File "" , 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. 请补充以下代码,使程序可以正常运行:
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:
class CodeA:
def foo():
print("调用静态方法 foo()")
# 将 foo() 方法设置为静态方法
foo = staticmethod(foo)
代码B:
class CodeB:
def foo(cls):
print("调用类方法 foo()")
# 将 foo() 方法设置为类方法
foo = classmethod(foo)
答:其实正是因为设置静态方法和类方法过于讨人吐槽,因此 Python 的作者才开发出了函数修饰符的形式替代。
代码A:
class CodeA:
@staticmethod
def foo():
print("调用静态方法 foo()")
代码B:
class CodeB:
@classmethod
def foo(cls):
print("调用类方法 foo()")
@something
def f():
print("I love FishC.com!")
答:其实 Python 的修饰符就是一种优雅的封装,但要注意的是只可以在模块或类定义内对函数进行修饰,不允许修饰一个类。
一个修饰符就是一个函数,它将被修饰的函数做为参数,并返回修饰后的同名函数或其它可调用的东西。
@something
def f():
print("I love FishC.com!")
# 相当于
def f():
print("I love FishC.com!")
f = something(f)
代码清单:
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