十三、类与对象
1.对象 = 属性+方法
(1)类的定义及调用
对象是类的实例。
面向对象最重要的概念就是类(Class)和实例(Instance),必须牢记类是抽象的模板,比如Student类,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同。
类的组成由属性和方法组成。
类的内部可以定义属性和方法,类的外部则可以直接调用属性或方法来操作数据。
类主要定义对象的结构,然后以类为模板创建对象。类不但包含方法定义,而且还包含所有实例共享的数据。
- 封装:信息隐蔽技术
用关键字 class 定义 Python 类,关键字后面紧跟类的名称、分号和类的实现。
分号是啥??
【例子:】
定义一个类Turtle:
class Turtle: #类名第一个字母大写,turtle是龟的意思
# 属性
color = 'green'
weight = 10
legs = 4
shell = True #shell 壳
mouth = '大嘴'
#方法
def climb(self): #这里的self是啥
print('我正在很努力的向前爬...')
def run(self):
print('我正在飞快的向前跑...')
def bite(self):
print('咬死你咬死你!!')
def eat(self):
print('有得吃,真满足...')
def sleep(self):
print('困了,睡了,晚安,zzz')
tt = Turtle()
print(tt)
# <__main__.Turtle object at 0x0000007C32D67F98>
print(type(tt))
# <class '__main__.Turtle'>
print(tt.__class__)
# <class '__main__.Turtle'>
输出tt的类名:
print(tt.__class__.__name__)
# Turtle
调用类中的方法:
tt.climb()
# 我正在很努力的向前爬...
tt.run()
# 我正在飞快的向前跑...
tt.bite()
# 咬死你咬死你!!
python的类也是对象,(前面说过对象是类的实例),它们是type类的实例。
比如int就是type的实例。
print(type(Turtle))
# <class 'type'>
关于上面用到的__class__,name
可以参考:
(a)python的常用内建方法
参考:Python常用内建方法:init,new,__class__的使用详解
(2)关于继承
类名一般是大写开头的单词,比如Turtle代表Turtle类。紧接着是(object),表示该类是从哪个类继承下来的。如果没有合适的继承类,就用object类。
【例子:】
class Turtle(object):
pass
- 继承:子类自动共享父类之间数据和方法的机制
【例子:】定义一个Mylist类,从父类list继承。
class MyList(list):
pass
lst = MyList([1, 5, 2, 7, 8])
lst.append(9)
lst.sort()
print(lst)
# [1, 2, 5, 7, 8, 9]
[1, 2, 5, 7, 8, 9, 1, 2, 3]
由此可见Mylist自动共享了父类list的方法。
(3)多态
- 多态:不同对象对同一方法响应不同的行动
【例子】
class Animal:
def run(self):
raise AttributeError('子类必须实现这个方法')
"""我们可以使用raise语句自己触发异常"""
class People(Animal):
def run(self):
print('人正在走')
class Pig(Animal):
def run(self):
print('pig is walking')
class Dog(Animal):
def run(self):
print('dog is running')
def func(animal):
animal.run()
"""下面调用试试"""
func(Pig())
# pig is walking
func(Animal())
#AttributeError: 子类必须实现这个方法
这说明不同的对象对同一个方法.run()响应了不同的行动。
这里都涉及到了self,但是self是什么?
2.self
python的self相当于C++的this
指针。
什么是this指针?
在 C++ 中,每一个对象都能通过 this 指针来访问自己的地址。this
指针是所有成员函数的隐含参数。因此,在成员函数内部,它可以用来指向调用对象。
【例子】
class Test:
def prt(self):
print(self)
print(self.__class__)
t = Test()
t.prt()
# <__main__.Test object at 0x000000BC5A351208>
# <class '__main__.Test'>
和下面这个有什么区别?
Test.prt(1)
#1
#<class 'int'>
类的方法与普通的函数只有一个特别的区别 —— 它们必须有一个额外的第一个参数名称(对应于该实例,即该对象本身),按照惯例它的名称是 self。在调用方法时,我们无需明确提供与参数 self 相对应的参数。
【例子】
class Ball:
def setName(self, name):
self.name = name
def kick(self):
print("我叫%s,该死的,谁踢我..." % self.name)
self.name = name这句话有什么用?
self.name=name这句话就是把外部传来的参数name值赋值给Ball类内自己的变量name。如下:
a = Ball()
a.setName("球A")
b = Ball()
b.setName("球B")
c = Ball()
c.setName("球C")
a.kick()
# 我叫球A,该死的,谁踢我...
b.kick()
# 我叫球B,该死的,谁踢我...
如上面的例子,通过setName这个方法(self.name = name)把外部的参数“球A”传给了Ball类内自己的变量name.
3.python的魔法方法
- 据说,Python 的对象天生拥有一些神奇的方法,它们是面向对象的 Python 的一切…
- 它们是可以给你的类增加魔力的特殊方法…
- 如果你的对象实现了这些方法中的某一个,那么这个方法就会在特殊的情况下被 Python 所调用,而这一切都是自动发生的…
- 类有一个名为
__init__(self[, param1, param2...])
的魔法方法,该方法在类实例化时会自动调用。
还记得上面定义的类Ball吗,没有用魔法方法的时候,实例化需要调用方法setName:
a = Ball()
a.setName("球A")
但是现在已经使用了魔法方法__init__,所以我们只需要
class Ball:
def __init__(self, name):
self.name = name
def kick(self):
print("我叫%s,该死的,谁踢我..." % self.name)
a = Ball("球A") #注意这里的区别
b = Ball("球B")
c = Ball("球C")
a.kick()
# 我叫球A,该死的,谁踢我...
b.kick()
# 我叫球B,该死的,谁踢我...
【例子】
class Turtle:
def __init__(self, x):
self.num = x
#这样的话传入的是乌龟的数量,调用的时候Turtle(3)就行,代表3只乌龟
def speak(self):
print("我们%d只乌龟在赛跑" % self.num)
Turtle(2).speak() #我们2只乌龟在赛跑
实例也是可以调用类方法吗?
4.公有与私有(待完善)
在 Python 中定义私有变量只需要在变量名或函数名前加上“__”两个下划线,那么这个函数或变量就会为私有的了。
【例子】之后补充。
5.继承
这里还没有看懂
6.组合
首先定义三个类,乌龟、🐟、水池,用到了魔法方法:
class Turtle:
def __init__(self, x):
self.num = x
#这样的话传入的是乌龟的数量,调用的时候Turtle(3)就行,代表3只乌龟
#这里只是定义了一种方法来传入乌龟的数量,并没有实际的“动作”
class Fish:
def __init__(self, x):
self.num = x
class Pool:
def __init__(self, x, y): #这里的self是说泳池自己
self.turtle = Turtle(x)
self.fish = Fish(y)
def print_num(self):
print("水池里面有乌龟%s只,小鱼%s条" % (self.turtle.num, self.fish.num))
上面的x,y和乌龟speak里的x一样,都是在调用时需要传入的参数。
p = Pool(2, 3)
p.print_num()
# 水池里面有乌龟2只,小鱼3条
练习题
1、以下类定义中哪些是类属性,哪些是实例属性?
class C:
num = 0
def __init__(self):
self.x = 4
self.y = 5
C.count = 6
【答】.count是类属性
.x,.y是实例属性
2、怎么定义私有⽅法?
【答】在方法名前加上英文双下划线:__
3、尝试执行以下代码,并解释错误原因:
class C:
def myFun():
print(‘Hello!’)
c = C()
c.myFun()
4、按照以下要求定义一个游乐园门票的类,并尝试计算2个成人+1个小孩平日票价。
要求:
- 平日票价100元
- 周末票价为平日的120%
- 儿童票半价
【答】
class Ticket():
#属性
adult_ticket_normal =100
adult_ticket_weekends = adult_ticket_normal *1.2
child_ticket_normal = adult_ticket_normal
child_ticket_weekends = child_ticket_normal*1.2
def calculation(adult_ticket_normal,child_ticket_normal):
s = adult_ticket_normal*2 + child_ticket_normal
return(s)
调用:
Ticket.calculation(100,50) #250
有个疑问,就是我已经在内部函数里定义了变量100,但是外部还是需要传入参数,这是什么原因。
十四、魔法方法
魔法方法总是被双下划线包围,例如__init__。
魔法方法是面向对象的 Python 的一切,如果你不知道魔法方法,说明你还没能意识到面向对象的 Python 的强大。
魔法方法的“魔力”体现在它们总能够在适当的时候被自动调用。
魔法方法的第一个参数应为cls(类方法) 或者self(实例方法)。
- cls:代表一个类的名称
- self:代表一个实例对象的名称
1.基本的魔法方法
__init__(self[, ...])
构造器,当一个实例被创建的时候调用的初始化方法
class Rectangle: #定义了一个长方形/矩形类
def __init__(self, x, y):
self.x = x
self.y = y
def getPeri(self):
return (self.x + self.y) * 2 #周长
def getArea(self):
return self.x * self.y
rect = Rectangle(4, 5) #rect是矩形结构的意思
print(rect.getPeri()) # 18
print(rect.getArea()) # 20
这里注意到了一个特殊的__init__方法,是做什么用的?
详细解读python中的__init__方法。
简单来说,__init__可以完成一些初始化的设定。
有了它,我们写Rectangle(4, 5) ,4,5这两个参数就会自动传入self.x和self.y中,如果没有,则会报错。
(不知道我理解的对不对?)
__new__(cls[, ...])
在一个对象实例化的时候所调用的第一个方法,在调用__init__
初始化前,先调用__new__
。__new__
至少要有一个参数cls,代表要实例化的类,此参数在实例化时由 Python 解释器自动提供,后面的参数直接传递给__init__
。__new__
对当前类进行了实例化,并将实例返回,传给__init__
的self。但是,执行了__new__
,并不一定会进入__init__
,只有__new__
返回了,当前类cls的实例,当前类的__init__
才会进入。
参考:
1.https://www.liaoxuefeng.com/wiki/1016959663602400/1017496031185408
2.https://github.com/datawhalechina/team-learning-program/blob/master/PythonLanguage/13.%20%E7%B1%BB%E4%B8%8E%E5%AF%B9%E8%B1%A1.md