类和对象
1. 一个例子
# 对象 = 属性 + 方法
# Python 中的类名称约定首字母大写,而 Python 函数名首字母小写
class Turtle:
#属性
color = 'green'
weight = 10
legs = 4
shell = True
mouth = '大嘴'
# 方法
# 这里的 self 表示 this 指针
def climb(self):
print("我正在很努力地向前爬")
def run(self):
print("我正在飞快地向前跑")
def bite(self):
print("我咬死你咬死你")
def eat(self):
print("有得吃,真满足!")
def sleep(self):
print("困了,睡觉,晚安")
tt = Turtle()
tt.climb()
tt.bite()
tt.sleep()
我正在很努力地向前爬
我咬死你咬死你
困了,睡觉,晚安
2. 面向对象的特征
① 封装
# 我们只知道这些方法如何调用,并不知道方法内部怎么实现的
list1 = [2,1,7,5,3]
list1.sort()
list1.append(9)
print(list1)
[1, 2, 3, 5, 7, 9]
② 继承
class MyList(list): # 这里表示 Mylist 类继承于 list 类
# pass 为占位符,表明不做任何操作
pass
list2 = MyList()
# list 类中的方法都被继承了
list2.append(1)
list2.append(7)
list2.append(5)
list2.append(4)
list2.append(9)
list2.sort()
print(list2)
[1, 4, 5, 7, 9]
③ 多态
# 不同对象对同一方法响应不同的行动
class A:
def fun(self):
print("我是小A")
class B:
def fun(self):
print("我是小B")
a = A()
b = B()
a.fun()
b.fun()
我是小A
我是小B
3. self
# 当一个对象的方法被调用时,会将自身作为参数传入
# 类的方法定义时,必须把 self 写入第一个参数
class Ball:
def setName(self,name):
self.name = name
def kick(self):
print("我叫%s,该死的,谁踢我。。。" % self.name)
a = Ball()
a.setName('球A')
b = Ball()
b.setName('球B')
c = Ball()
c.setName('球C')
a.kick()
b.kick()
c.kick()
我叫球A,该死的,谁踢我。。。
我叫球B,该死的,谁踢我。。。
我叫球C,该死的,谁踢我。。。
4. _ _ init _ _ (self)(这里是两个双下划线)
class Ball:
# 这个即为构造函数
def __init__(self,name):
self.name = name
def kick(self):
print("我叫%s,该死的,谁踢我。。。" % self.name)
b = Ball('土豆')
b.kick()
我叫土豆,该死的,谁踢我。。。
5. 公有和私有
class Person:
name = '小甲鱼'
p = Person()
print(p.name)
小甲鱼
class Person:
# 变量前加上双下划线就会变成私有变量,无法直接调用
__name = '小甲鱼'
# 可以通过方法来调用
def getName(self):
return self.__name
p = Person()
print(p.name)
-------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-23-e128ce7b44b9> in <module>()
5 return self.__name
6 p = Person()
----> 7 print(p.name)
AttributeError: 'Person' object has no attribute 'name'
print(p.getName())
小甲鱼
# 其实 Python 的私有为伪私有,只是将变量的名字改了(名字改编)
# 可以通过 对象名._类名__变量名 来调用
print(p._Person__name)
小甲鱼
6. 继承
class Parent:
def hello(self):
print("正在调用父类的方法。。。")
class Child(Parent):
pass
p = Parent()
p.hello()
c = Child()
c.hello()
正在调用父类的方法。。。
正在调用父类的方法。。。
class Child1(Parent):
# 如果在子类中定义与父类同名的方法或属性,则会覆盖父类相应的方法或属性
def hello(self):
print("正在调用子类的方法。。。")
d = Child1()
d.hello()
p.hello()
正在调用子类的方法。。。
正在调用父类的方法。。。
7. 继承例子
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 Carp(Fish):
pass
class Salmon(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()
我现在的位置是: 3 6
fish.move()
我现在的位置是: 2 6
goldfish = Goldfish()
fish.move()
我现在的位置是: 1 6
shark = Shark()
shark.eat()
吃货的梦想就是天天有得吃
# 由于 Shark 子类重写了 __init__ 方法,属性 x 和 y,都不存在了,所以会报错
shark.move()
-------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-44-e54bb3f218a2> in <module>()
----> 1 shark.move()
<ipython-input-35-3f1e9b36930e> in move(self)
5 self.y = r.randint(0,10)
6 def move(self):
----> 7 self.x -= 1
8 print("我现在的位置是:", self.x, self.y)
9
AttributeError: 'Shark' object has no attribute 'x'
class Shark1(Fish):
def __init__(self):
# 这里调用未绑定的父类方法
# 这里的 self 是子类的对象,即将子类对象作为参数传入父类的构造方法
# 从而定义了属性 x 和 y
Fish.__init__(self)
self.hungry = True
def eat(self):
if self.hungry:
print("吃货的梦想就是天天有得吃")
self.hungry = False
else:
print("太撑了,吃不下了!")
shark1 = Shark1()
shark1.eat()
shark1.move()
吃货的梦想就是天天有得吃
我现在的位置是: 8 9
class Shark2(Fish):
def __init__(self):
# 也可以使用super
super().__init__()
self.hungry = True
def eat(self):
if self.hungry:
print("吃货的梦想就是天天有得吃")
self.hungry = False
else:
print("太撑了,吃不下了!")
shark2 = Shark2()
shark2.eat()
shark2.move()
吃货的梦想就是天天有得吃
我现在的位置是: 5 9
8. 多重继承
# 尽量避免使用多重继承
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()
c.foo2()
我是foo1,我为Base1代言。。。
我是foo2,我为Base2代言。。。
9. 组合
class Turtles:
def __init__(self, x):
self.num = x
class Fishs:
def __init__(self, x):
self.num = x
class Pool:
def __init__(self, x, y):
# 把类的实例化放到一个新类中,这样就可以避免使用多重继承
self.turtles = Turtles(x)
self.fishs = Fishs(y)
def print_num(self):
print("水池里总共有乌龟 %d 只,小鱼 %d 条!" % (self.turtles.num, self.fishs.num))
pool = Pool(1,10)
pool.print_num()
水池里总共有乌龟 1 只,小鱼 10 条!
10. Mix-in 编程机制
见 https://fishc.com.cn/forum.php?mod=viewthread&tid=48888&highlight=Mix
11. 类、类对象和实例对象
# 在定义的时候他是一个类,等他定义完就是一个类对象
class C:
count = 0
a = C()
b = C()
c = C()
print(a.count)
print(b.count)
print(c.count)
0
0
0
# 这里相当于生成了一个对象来覆盖了原来的count
# 类中定义的属性都是静态属性
# 类属性和类对象是相互绑定的,并不依赖于实例对象
# 当执行 c.count += 10 的时候,c实例对象里面多出了一个count属性
# 这个实例属性将类属性给覆盖了
c.count += 10
print(c.count)
print(a.count)
print(b.count)
10
0
0
C.count += 100
print(a.count)
print(b.count)
print(c.count)
100
100
10
12. 属性和方法同名
class D:
def x(self):
print("X-man!")
d = D()
d.x()
X-man!
# 这里是在实例对象里创建一个新的变量x,与方法同名,会覆盖方法
d.x = 1
print(d.x)
1
d.x()
-------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-84-a03010f95e9e> in <module>()
----> 1 d.x()
TypeError: 'int' object is not callable
- 不要试图在一个类里边定义出所有能想到的特性和方法,应该利用继承和组合机制来进行扩展。
- 用不同的词性命名,如属性名用名词,方法名用动词。
13. 绑定
Python要求方法需要有实例才能被调用,即为绑定
class BB:
# 这里没写参数 self
def printBB():
print("no zuo no die")
# 直接用类名可以调用
BB.printBB()
no zuo no die
# 由于没有 self 参数,所以实例化对象没办法调用该方法
bb = BB()
bb.printBB()
-------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-87-65bc6f83f641> in <module>()
1 bb = BB()
----> 2 bb.printBB()
TypeError: printBB() takes 0 positional arguments but 1 was given
class CC:
# 方法是静态的,绑定在类对象中
def setXY(self, x, y):
self.x = x
self.y = y
def printXY(self):
print(self.x, self.y)
dd = CC()
# 以字典方式输出 dd 对象所有的属性
print(dd.__dict__)
# 只有实例对象的属性,不显示类属性和特殊属性(魔法方法)
# 键用引号引起来,表示属性名
# 值表明属性对应的值
print(CC.__dict__)
{}
{'__module__': '__main__', 'setXY': <function CC.setXY at 0x00000159506896A8>, 'printXY': <function CC.printXY at 0x00000159506891E0>, '__dict__': <attribute '__dict__' of 'CC' objects>, '__weakref__': <attribute '__weakref__' of 'CC' objects>, '__doc__': None}
# 这里调用实际上调用的是dd.setXY(dd,4,5)
# x,y 变量储存在 dd 实例对象的空间中
dd.setXY(4,5)
# 这里的 x, y 仅属于dd
print(dd.__dict__)
print(CC.__dict__)
{'x': 4, 'y': 5}
{'__module__': '__main__', 'setXY': <function CC.setXY at 0x00000159506896A8>, 'printXY': <function CC.printXY at 0x00000159506891E0>, '__dict__': <attribute '__dict__' of 'CC' objects>, '__weakref__': <attribute '__weakref__' of 'CC' objects>, '__doc__': None}
del CC
ee = CC()
-------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-99-55ea04a3e7cb> in <module>()
1 del CC
----> 2 ee = CC()
NameError: name 'CC' is not defined
# 虽然类对象已经被删除,但是实例对象已经储存在内存中,仍然可以使用
# 只有在程序退出时才会被释放
# 所以大多数情况下应该使用实例属性,而不要去使用类属性
dd.printXY()
4 5
14.一些BIF
① issubclass(class,classinfo)
如果 clss 是 classinfo 的子类,就会返回TRUE
- 一个类会被认为是其自身的子类
- classinfo可以是类对象组成的元组,只要class是其中任何一个候选类的子类,则会返回True
class A:
pass
class B(A):
pass
class C:
pass
print(issubclass(B,A))
print(issubclass(B,B))
# 所有类都是object类的子类
print(issubclass(B,object))
print(issubclass(B,C))
True
True
True
False
② isinstance(object,classinfo)
如果 object 是 classinfo 的实例对象,就会返回TRUE
- 如果第一个参数不是对象,则永远返回False
- classinfo可以是类对象组成的元组,只要object是其中任何一个候选类的实例对象,则会返回True
- 如果第二个参数不是类或者由类对象组成的元组,会抛出一个TypeError
b1 = B()
print(isinstance(b1,B))
# B类继承于A类
print(isinstance(b1,A))
print(isinstance(b1,C))
print(isinstance(b1,(A,B,C)))
True
True
False
True
③ hasattr(object,name)
测试object对象里面是否有属性name
class D:
def __init__(self,x=0):
self.x = x
d1 = D()
# 这里的属性名参数,必须加上引号,否则会报错
print(hasattr(d1,'x'))
True
④ getattr(object,name[,default])
返回对象指定的属性值,如果属性不存在,如果你还设置了default,则会打印出default;如果没设置,则会抛出AttributeError的异常
print(getattr(d1,'x'))
0
print(getattr(d1,'y'))
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-12-bae8070f3f96> in <module>()
----> 1 print(getattr(d1,'y'))
AttributeError: 'D' object has no attribute 'y'
print(getattr(d1,'y','您所访问的属性不存在'))
您所访问的属性不存在
⑤ setattr(object,name,value)
设置对象中指定属性的值,如果没有,则会新建并赋值
setattr(d1,'y','Nigream')
print(getattr(d1,'y'))
Nigream
⑥ delattr(object,name)
删除对象中指定的属性,如果不存在则抛出AttributeError的异常
delattr(d1,'y')
delattr(d1,'y')
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-16-88d9b933b227> in <module>()
----> 1 delattr(d1,'y')
AttributeError: y
⑦ property(fget=None, fset=None, fdel=None, doc=None)
通过属性来设置属性
- fget为获取属性的方法,当执行e1.x语句时调用
- fset为设置属性的方法,当执行e1.x=18语句时调用
- doc为删除属性的方法,当执行del e1.x语句时调用
class E:
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)
e1 = E()
print(e1.getSize())
10
print(e1.x)
10
e1.x = 18
print(e1.x)
18
print(e1.getSize())
18
del e1.x
e1.size
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-53-2b22d88d2a9b> in <module>()
----> 1 e1.size
AttributeError: 'E' object has no attribute 'size'