目录
七、类的多态(polymorphism)与继承(inheritance)
类(对象)
列表可以收集变量(数据),函数可以把一些代码收集到能够反复使用的单元中。
对象可以把函数和数据收集在一起。
一、定义
对象考虑方面:
- 可以对它们做什么(动作)。
- 如何描述(属性或特性)。
以球为例
球的属性:
ball.color
ball.size
ball.weight
球的描述:
ball.kick()
ball.throw()
ball.inflate()
得出:
===========================================================
类是有着共同特征和行为事物的抽象概念的总和
class CocaCola:
formula = ['caffeine', 'sugar', 'water', 'soda']
formula是类的变量,即类的属性
二、创建类(对象)
- 第一步:定义对象看上去什么样,会做什么?即它的属性和方法。
- 第二步:使用类来创建一个真正的对象,这个对象被称为这个类的一个实例
注意:Python 中的另一个约定是类名总是以大写字母开头。
三、类的实例化( 创建一个对象实例 )
类的实例化/实例:左边创建一个变量,右边写上类的名称。被实例化后的对象,称之为实例化。
myBall = Ball()
myBall.direction = "down"
myBall.color = "green"
myBall.size = "small"
myBall.bounce()
coke_for_me = CocaCola()
coke_for_you = CocaCola()
print(CocaCola.formula)
print(coke_for_me.formula)
print(coke_for_you.formula)
输出:
['caffeine', 'sugar', 'water', 'soda']
['caffeine', 'sugar', 'water', 'soda']
['caffeine', 'sugar', 'water', 'soda']
三、类属性的引用
类名字后输入 . ,延伸出定义类时里面的属性——即类属性的引用
点记法( . ):
ball.olor
类的属性会被所有类的实例共享
四、实例属性
从下列可以看出
coke_for_China = CocaCola()
coke_for_China.local_logo = '可口可乐'
print(coke_for_China.local_logo)
print(coke_for_China)
输入:
可口可乐
<__main__.CocaCola object at 0x000002F6D55F27F0>
五、实例方法
class CocaCola:
formula = ['caffeine', 'sugar', 'water', 'soda']
def drink(self):
print('Energy!')
coke = CocaCola()
coke.drink()
输出:
Energy!
问题一: 什么是self?
在类属性和方法定义中多处出现了“self”
ANS:
修改方法的参数,可以看出self其实就是被创建的实例本身。
这是 Python 处理对象的另外一个“魔法”。调用一个类方法时,究竟是哪个实例调用了这个方法?这个信息(也就是实例引用)会自动传递给方法。
即被实例化的对象会被IDE传入方法,作为第一个参数,如图:
self这个参数名称可以随意修改,一般使用self
class CocaCola:
formula = ['caffeine', 'sugar', 'water', 'soda']
def drink(coke):
print('Energy!')
coke = CocaCola()
coke.drink()
输出:
Energy!
更多参数:
类的方法也可以有自己的参数
例子1:
class CocaCola:
formula = ['caffeine', 'sugar', 'water', 'soda']
def drink(self, how_much):
if how_much == 'a sip':
print('Cool~')
elif how_much == 'whole bottle':
print('Headache!')
ice_coke = CocaCola()
ice_coke.drink('a sip')
输出:
Cool~
例子2:
class Ball:
def bounce(self):
if self.direction == 'down':
self.direction = 'up'
myBall = Ball()
myBall.direction = 'down'
myBall.color = 'red'
myBall.size = 'small'
print(myBall.direction)
print(myBall.color)
print(myBall.size)
myBall.bounce()
print(myBall.direction)
输出:
down
red
small
up
六、“魔术方法”
魔术方法:
✍初始化对象
创建类定义时,可以定义一个特定的方法,名为 __init__(),只要创建这个类的一个新实例,就会运行这个方法。可向 __init__() 方法传递参数,这样创建实例时就会把属性设置为你希望的值。
如 __init__()
__init__() (initialize初始化):在类中定义其,在创建实例的时候可以自动帮忙处理不少事情,如增加实例属性,在【四、实例属性】中是定义完类之后再做的,而这里可以一次到位。
也可以意味着在创建实例时不去引用init()方法,其中命令也被自动地执行
即
__init__() 方法会在对象创建时完成初始化。每个对象都内置有一个 __init__() 方法。如果在类定义中没有加入自己的 __init__() 方法,就会有这样一个内置方法接管,它的工作就是创建对象。
例子:
class Ball:
def __init__(self, color, size, direction):
self.color = color
self.size = size
self.direction = direction
def bounce(self):
if self.direction == 'down':
self.direction = 'up'
myBall = Ball("red", "big", "down")
print(myBall.direction)
print(myBall.color)
print(myBall.size)
myBall.bounce()
print(myBall.direction)
输出:
down
red
big
up
例一:
class CocaCola:
formula = ['caffeine', 'sugar', 'water', 'soda']
def __init__(self):
self.local_logo = '可口可乐'
def drink(self):
print('Energy!')
coke = CocaCola()
print(coke.local_logo)
输出:
可口可乐
例二:
class CocaCola:
formula = ['caffeine', 'sugar', 'water', 'soda']
def __init__(self):
for element in self.formula:
print('Coke has {}'.format(element))
def drink(self):
print('Energy!')
coke = CocaCola()
输出:
Coke has caffeine
Coke has sugar
Coke has water
Coke has soda
也可以有自己的参数:
class CocaCola:
formula = ['caffeine', 'sugar', 'water', 'soda']
def __init__(self, logo_name):
self.local_logo = logo_name
def drink(self, how_much):
if how_much == 'a sip':
print('Cool~')
elif how_much == 'whole bottle':
print('Headache!')
coke = CocaCola('可口可乐')
coke.local_logo
print(coke.local_logo)
输出:
可口可乐
注意点:
✍魔术方法:__str__()
魔术方法:创建类时 Python 自动包含的一些方法。Python 程序员通常把它们叫做特殊方法(special method)。
__str__(),会告诉 Python 打印(print)一个对象时具体显示什么内容。
Python 会默认以下内容。
- 实例在哪里定义(在 __main__ 中,这是程序的主部分)。
- 类名( Ball)。
- 存储实例的内存位置( 0x00BB83A0 部分)。
当然,可以自定义,可以定义自己的 __str__(),这会覆盖内置的 __str__() 方法
class Ball:
def __init__(self, color, size, direction):
self.color = color
self.size = size
self.direction = direction
def bounce(self):
if self.direction == 'down':
self.direction = 'up'
myBall = Ball("red", "big", "down")
print(myBall)
输出:
<__main__.Ball object at 0x00000272CC1FA640>
自定义:
class Ball:
def __init__(self, color, size, direction):
self.color = color
self.size = size
self.direction = direction
def __str__(self):
msg = "Hi, i am a " + self.size + " " + self.color + "ball!"
return msg
def bounce(self):
if self.direction == 'down':
self.direction = 'up'
myBall = Ball("red", "big", "down")
print(myBall)
输出:
Hi, i am a big redball!
七、类的多态(polymorphism)与继承(inheritance)
ⅰ、多态——多态—同一个方法,不同的行为
多态是指对于不同的类,可以有同名的两个(或多个)方法。
例如:
分别使用getArea()计算
myTriangle = Triangle(4,5)
mySquare = Square(7)
>>> myTriangle.getArea()
10.0
>>> mySquare.getArea()
49
>>>
ⅱ 继承——向父母学习
从其他类继承属性或方法的类称为派生类(derived class)或子类(subclass)
例如:
在新类 CaffeineFree 后面括号中加入 CocaCola ,表示CaffeineFree类继承于CocaCola类。CaffeineFree成为CocaCola子类,CocaCola类中的变量和方法完全可以被CaffeineFree子类继承,如需要特殊改动可进行覆盖
class CocaCola:
calories = 140
sodium = 45
total_carb = 39
caffeine = 34
ingredients = [
'High Fructose Corn Syrup',
'Carbonated Water',
'Phosphoric Acid',
'Natural Flavors',
'Caramel Color',
'Caffeine'
]
def __init__(self, logo_name):
self.local_logo = logo_name
def drink(self):
print('You got {} cal energy !'.format(self.calories))
class CaffeineFree(CocaCola):
caffeine = 0
ingredients = [
'High Fructose Corn Syrup',
'Carbonated Water',
'Phosphoric Acid',
'Natural Flavors',
'Caramel Color',
]
coke_a = CaffeineFree('Cocacola-FREE')
coke_a.drink()
输出:
You got 140 cal energy !
或
🌞令人困惑的类属性与实例属性相关问题
问题一:类属性如果被重新赋值,是否会影响到类属性的引用?
class TestA:
attr = 1
obj_a = TestA()
TestA.attr = 42
print(obj_a.attr)
输出:
42
问题二: 实例属性如果被重新赋值,是否会影响到类属性的引用?
class TestA:
attr = 1
obj_a = TestA()
obj_b = TestA()
obj_a.attr = 42
print(obj_b.attr)
输出:
1
问题三: 类属性实例属性具有相同的名称,那么 . 后面引用的将会是什么?
class TestA:
attr = 1
def __init__(self):
self.attr = 42
obj_a = TestA()
print(obj_a.attr)
输出:
42
🌺 __dict__ :是隐藏在类的特殊属性,是一个字典,用于储存类或实例属性。默认隐藏的
class TestA:
attr = 1
def __init__(self):
self.attr = 42
obj_a = TestA()
print(obj_a.attr)
print(TestA.__dict__)
print(obj_a.__dict__)
输出:
{'__module__': '__main__', 'attr': 1, '__init__': <function TestA.__init__ at 0x000001819F1ECC10>, '__dict__': <attribute '__dict__' of 'TestA' objects>, '__weakref__': <attribute '__weakref__' of 'TestA' objects>, '__doc__': None}
{'attr': 42}
🌺 python中属性的引用机制——自外而内
八、类的扩展理解
obj1 =1
obj2 = 'String'
obj3 = []
obj4 = {}
print(type(obj1), type(obj2), type(obj3), type(obj4))
输出:
<class 'int'> <class 'str'> <class 'list'> <class 'dict'>
python中任何类的对象都是类的实例
九、 一个示例类 -- HotDog
热狗的属性:
- cooked _level:这是一个数字,通过这个属性我们可以知道热狗烤了多长时间。
- 0 ~ 3 表示还是生的,超过 3 表示半生不熟,超过 5 表示已经烤好,超过 8 表示已经烤成木炭了!我们的热狗开始时是生的。
- cooked_string:这是一个字符串,描述热狗的生熟程度。
- condiments :这是热狗上的配料列表,比如番茄酱、芥末酱等。
热狗的方法:
- cook() :把热狗烤一段时间。这会让热狗越来越熟。
- add_condiment():给热狗加一些配料。
- __ init__():创建实例并设置默认属性。
- __ str__():让 print 的结果看起来更好一些。
class HotDog:
def __init__(self):
self.cooked_level = 0
self.cooked_string = 'Raw'
self.condiments = []
def __str__(self):
msg = "hot dog"
if len(self.condiments) > 0:
msg = msg + "with"
for i in self.condiments:
msg = msg + i + ","
msg = msg.strip(",")
msg = self.cooked_string + " " + msg + "."
return msg
def cook(self, time):
self.cooked_level = self.cooked_level + time
if self.cooked_level > 8:
self.cooked_string = 'Charcoal'
elif self.cooked_level > 5:
self.cook_string = 'Well_done'
elif self.cooked_level > 3:
self.cooked_string = "Medium"
else:
self.cooked_string = 'Raw'
def addCondiment(self, condiment):
self.condiments.append(condiment)
myDog = HotDog()
print(myDog.cooked_level)
print(myDog.cooked_string)
print(myDog.condiments)
print("Cooking hot dog for 4 minutes")
myDog.cook(4)
print(myDog)
print("Cooking hot dog for 4 minutes")
myDog.cook(3)
print(myDog)
print("what happening if i cook it for 10 more minutes")
myDog.cook(3)
print(myDog)
print("Now, i am going to add some stuff on my hot dog")
myDog.addCondiment("ketchup")
myDog.addCondiment("mustard")
print(myDog)
十、注意点——未雨绸缪
在例子中的方法没有加入任何实际代码,只有一些注释来解释这些方法要做什么。这是一种未雨绸缪的方法。这是对以后要增加的内容提前做出计划或提前考虑。但运行起来会报错。
解决方法:
“空”函数或方法称为代码桩(code stub)。
在注解前添加 pass 关键字作为占位符