Commit e3ee6e13 by 前钰

Upload New File

parent 65e3d6dc
{
{
"cells": [
{
"cell_type": "markdown",
"id": "535c3112",
"metadata": {},
"source": [
"# 封装\n",
"\n",
"一般,使用class语句来创建一个新类,class之后为类的名称(通常首字母大写)并以冒号结尾,代码如下:"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "c526a243",
"metadata": {},
"outputs": [],
"source": [
"class Car(): \n",
" # 方法 \n",
" def getCarInfo(self): \n",
" print('车轮子个数:%d, 颜色%s'%(self.wheelNum, self.color)) \n",
" def move(self): \n",
" print(\"车正在移动...\")"
]
},
{
"cell_type": "markdown",
"id": "845cadd6",
"metadata": {},
"source": [
"在类中,可以定义所使用的方法,类中的方法与普通的函数只有一个特别的区别:它们必须有一个额外的第一个参数名称, 按照惯例它的名称是self。它表示类创建的实例本身,指向当前创建对象的内存地址。某个对象调用其方法时,Python解释器会把这个对象作为第一个参数传递给self,所以开发者只需要传递后面的参数即可。\n",
"\n",
"还是举之前提到的盖房子的例子:假设我们由“商品房”这个类实例化出对象a和对象b,可以理解成客户a和客户b。这时候客户a和客户b都买了同一个设计图(同一个类)盖出的房子,但是他们想要自己的装修风格,这些装修风格就是对象a、对象b的特征。当装修队开始装修房子时,要根据不同风格进行装修。那么,如何选择装修风格呢?很简单,根据客户选择!所以,self代指的就是客户,也就是实例本身。\n",
"\n",
"所以,具体的实例化过程是这样的,当使用商品房这个类实例化出对象a时,默认参数self会把对象a创建在某一个内存地址中;同理,在实例化出对象b时,默认参数self会把对象b创建在另一个与a不同的内存地址中。就如同客户a和客户b买商品房不可能买的是同一套房子一样,self的制作就如同售楼处,可以让客户买到不同地址的房子,而且还会管理这些房号,让客户在装修时知道去几零几号装修。简而言之,可以把self理解成类在实例化对象时的管理工具。\n",
"\n",
"刚才的代码中,我们定义了一个“车类(Car)”;就好比有了一个张车的图纸,那么接下来就应该把图纸交给生成工人们去生成了。Python中,可以根据已经定义的类去创建出一个个对象,创建对象的格式为“对象名 = 类名()”"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "e9071352",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"车在行驶...\n",
"white\n",
"4\n"
]
}
],
"source": [
"class Car: \n",
" # 定义移动方法 \n",
" def move(self): \n",
" print('车在行驶...') \n",
" # 定义鸣笛方法 \n",
" def toot(self): \n",
" print(\"车在嘟嘟..\") \n",
"# 创建一个对象,并用变量Wuling来保存它的引用 \n",
"Wuling = Car() \n",
"Wuling.color = 'white' #使用‘.’符号的方法添加类属性:车的颜色 \n",
"Wuling.wheelNum = 4 #使用‘.’符号的方法添加类属性:轮子数量 \n",
"Wuling.move() #使用‘.函数名()’的语法调用类中函数:车的行驶 \n",
"print(Wuling.color) #打印实例五菱的颜色属性 \n",
"print(Wuling.wheelNum) #打印实例五菱的车轮数量属性"
]
},
{
"cell_type": "markdown",
"id": "e4cf990b",
"metadata": {},
"source": [
"在上面的实例中,我们给五菱神车添加了两个对象属性:wheelNum和color,试想一下,如果再次创建一个对象的话,创建之后需要重新进行属性添加,这样做是很麻烦的。那么是否可以在创建对象时,就顺便把属性也给予了呢?\n",
"这就是__init__()函数的作用。代码如下:"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "181933fc",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"五菱车的颜色为: white\n",
"五菱车轮胎数量为: 4\n",
"自行车的颜色为: black\n",
"自行车轮胎数量为: 2\n"
]
}
],
"source": [
"class Car: \n",
" # 初始化函数,对象属性有默认值:4和white;也可以通过传参的方法对对象属性进行重新赋值 \n",
" def __init__(self, wheelNum=4, color='white'): \n",
" self.wheelNum = wheelNum \n",
" self.color = color \n",
" # 定义类方法 \n",
" def move(self): \n",
" print('车在行驶') \n",
"# 创建对象五菱神车,不传参时,属性使用默认值 \n",
"Wuling = Car() \n",
"print('五菱车的颜色为:', Wuling.color) \n",
"print('五菱车轮胎数量为:', Wuling.wheelNum) \n",
"# 创建对象自行车,传参时,新的传参值代替默认值 \n",
"Bicycle = Car(2, 'black') \n",
"print('自行车的颜色为:', Bicycle.color) \n",
"print('自行车轮胎数量为:', Bicycle.wheelNum) \n"
]
},
{
"cell_type": "markdown",
"id": "dfcbd322",
"metadata": {},
"source": [
"注意:\n",
"\n",
"(1)当创建“Wuling对象”后,在没有调用方法的前提下,Wuling就默认拥有了2个属性:wheelNum和color,原因是方法是在创建对象后,就立刻被默认调用了__init__()函数,不需要手动调用。\n",
"\n",
"(2)__init__(self)中的self参数,不需要开发者传递,Python解释器会自动把当前的对象引用传递进去。"
]
},
{
"cell_type": "markdown",
"id": "ee70e676",
"metadata": {},
"source": [
"## 魔法方法\n",
"\n",
"魔法方法是在Python的类中被双下划线前后包围的方法。这些方法在类或对象进行特定的操作时会自动被调用,读者可以使用或重写这些魔法方法,给自定义的类添加各种特殊的功能来满足自己的需求。刚刚已经介绍过初始化魔法方法__init__,也是在定义类时最常见的魔法方法。除此之外,还有一些常见的魔法方法如下。"
]
},
{
"cell_type": "markdown",
"id": "9ec02d5c",
"metadata": {},
"source": [
"Python中,方法也是一种高等的对象。这意味着它们也可以像其它对象一样被传递到方法中,这是一个非常惊人的特性。Python中有一个特殊的魔术方法可以让类的实例的行为表现得像函数一样,你可以调用它们,将一个函数当作一个参数传到另外一个函数中等等。这个魔法方法就是__call__(self, [args…])。代码如下:"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "bc92e8f3",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"10 20\n",
"100 200\n"
]
}
],
"source": [
"class Circle(object): \n",
" def __init__(self, x, y): \n",
" self.x = x \n",
" self.y = y \n",
" def __call__(self, x, y): \n",
" self.x = x \n",
" self.y = y \n",
"a = Circle(10, 20) # __init__ \n",
"print(a.x, a.y) # 10 20 \n",
"a(100, 200) # 此时a这个对象可以当作一个方法来执行,这是__call__魔法方法的功劳 \n",
"print(a.x, a.y) # 100 200"
]
},
{
"cell_type": "markdown",
"id": "e26f651e",
"metadata": {},
"source": [
"这只是Python中魔法方法的几个简单例子。实际上,还有很多其他的魔法方法,用于处理各种操作和场景。了解这些方法并有效地使用它们可以帮助读者更好地定义自己的对象和类。"
]
},
{
"cell_type": "markdown",
"id": "59a9ef37",
"metadata": {},
"source": [
"## 类属性和实例属性\n",
"\n",
"类属性顾名思义就是类所拥有的属性,分为共有属性和私有属性,私有属性通过“__属性名称”的方法进行定义。对于公有的类属性,可以在类外进行访问,私有属性在类外不可以直接访问,代码如下:"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "e0c11a1b",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Arwin\n",
"Arwin\n"
]
}
],
"source": [
"class People(object): \n",
" name = 'Arwin' #公有的类属性 \n",
" __age = 24 #私有的类属性 \n",
" def __init__(self): \n",
" pass \n",
"p = People() \n",
"print(p.name) #正确 \n",
"print(People.name) #正确 \n",
"# print(p.__age) #错误,不能在类外通过实例对象访问私有的类属性 \n",
"# print(People.__age) #错误,不能在类外通过类对象访问私有的类属性 \n",
"# print(p._People__age) #这种特殊的访问方法可以在类外访问私有的类属性"
]
},
{
"cell_type": "markdown",
"id": "ac4e9c80",
"metadata": {},
"source": [
"注意:类属性是声明在类的内部,实例方法的外部的属性,即在class内,__init__(self)方法之前。\n",
"\n",
"实例属性是从属于实例对象的属性,也称为“实例变量”,要点如下:\n",
"\n",
"(1)实例属性一般在__init__()方法中通过如下代码定义“self.实例属性名 = 初始值”。\n",
"\n",
"(2)在本类的其他实例方法中,也是通过self进行访问“self.实例属性名”。\n",
"\n",
"(3)创建实例对象后,可通过实例对象访问,“obj = 类名()”创建和初始化对象,调用__init__()初始化属性,“obj.实例属性名 = 值”可以给已有属性赋值,也可以新加属性。\n",
"\n",
"(4)实例属性可修改、新增、删除。\n",
"\n",
"需要注意的是,如果在类外修改类属性,必须通过类对象去引用然后进行修改。\n",
"如果通过实例对象去引用修改,会产生一个同名的实例属性,这种方式修改的是实例属性,不会影响到类属性。当类属性与实例属性同名时,一个实例访问这个属性时实例属性会覆盖类属性,但类访问时不会。代码如下:"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "1e26e20e",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"China\n",
"China\n",
"Japan\n",
"China\n",
"UK\n"
]
}
],
"source": [
"class People(object): \n",
" country = 'China' #类属性 \n",
"# 访问类属性 \n",
"print(People.country) \n",
"# 实例化对象 \n",
"p = People() \n",
"# 访问实例属性 \n",
"print(p.country) \n",
"# 修改实例属性 \n",
"p.country = 'Japan' \n",
"# 访问实例属性,实例属性会屏蔽掉同名的类属性 \n",
"print(p.country) \n",
"# 访问类属性,会发现没有改变 \n",
"print(People.country) \n",
"#通过类对象去引用修改类属性 \n",
"People.country = \"UK\" \n",
"# 访问类属性 \n",
"print(People.country)"
]
},
{
"cell_type": "markdown",
"id": "490fba20",
"metadata": {},
"source": [
"## 实例方法、类方法和静态方法\n",
"\n",
"1)实例方法\n",
"\n",
"之前的例子中,在类中以def关键字定义的都可以称之为实例方法,不仅如此,类的初始化方法__init__()理论上也属于实例方法,只不过它比较特殊。实例方法最大的特点就是,它最少也要包含一个self参数,用于绑定调用此方法的实例对象“Python 会自动完成绑定”。实例方法通常会用类对象直接调用。\n",
"\n",
"2)类方法\n",
"\n",
"Python类方法和实例方法相似,它最少也要包含一个参数,只不过类方法中通常将其命名为cls,类方法是类对象所拥有的方法,需要用修饰器@classmethod来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以cls作为第一个参数,可以通过实例对象和类对象去访问。类方法还有一个用途就是可以对类属性进行修改。代码如下:"
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "dfbb3857",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"China\n",
"China\n",
"Japan\n",
"Japan\n"
]
}
],
"source": [
"class People(object): \n",
" country = 'China' \n",
" #类方法,用classmethod来进行修饰 \n",
" @classmethod \n",
" def getCountry(cls): \n",
" return cls.country \n",
" @classmethod \n",
" def setCountry(cls,country): \n",
" cls.country = country \n",
"p = People() \n",
"print(p.getCountry()) #可以用过实例对象引用 \n",
"print(People.getCountry()) #可以通过类对象引用 \n",
"p.setCountry('Japan') \n",
"print(p.getCountry()) \n",
"print(People.getCountry()) "
]
},
{
"cell_type": "markdown",
"id": "020975e5",
"metadata": {},
"source": [
"类方法什么时候使用呢?我们可以考虑一个场景。\n",
"假设有一个学生类和一个班级类,需实现的功能为:学生类继承自班级类,每实例化一个学生,班级人数都会增加。最后,需要实例化一些学生,并获取班级中的总人数。\n",
"思考:这个问题用类方法做比较合适,为什么?\n",
"我们要实例化的是学生,但是从学生实例中获取班级总人数,在逻辑上显然是不合理的。如果要获得班级总人数,生成一个班级实例是没有必要的。因此,编写一个类方法最为合适,这个方法能够访问并更新班级的总人数,而不需要创建班级实例。"
]
},
{
"cell_type": "markdown",
"id": "faa3faff",
"metadata": {},
"source": [
"3)静态方法\n",
"静态方法需要通过修饰器@staticmethod来进行修饰。静态方法是类中的函数。静态方法主要是用来存放逻辑性的代码,逻辑上属于类,但是和类本身没有关系,也就是说在静态方法中,不会涉及类中的属性和方法的操作。可以理解为,静态方法是个独立的、单纯的函数,它仅仅托管于某个类的名称空间中,便于使用和维护。例如,笔者想定义一个关于时间操作的类,其中有一个获取当前时间的函数,代码如下:"
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "f3af575f",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"15:13:50\n",
"15:13:50\n"
]
}
],
"source": [
"import time \n",
"class TimeTest(object): \n",
" def __init__(self, hour, minute, second): \n",
" self.hour = hour \n",
" self.minute = minute \n",
" self.second = second \n",
" @staticmethod \n",
" def showTime(): \n",
" return time.strftime(\"%H:%M:%S\", time.localtime()) \n",
"# 使用类对象调用静态方法 \n",
"print(TimeTest.showTime()) \n",
"# 实例化对象 \n",
"t = TimeTest(2, 10, 10) \n",
"# 使用实例对象调用静态方法 \n",
"print(t.showTime())"
]
},
{
"cell_type": "markdown",
"id": "6864713f",
"metadata": {},
"source": [
"如上,使用了静态方法,然而静态方法实现中并没使用实例的属性和方法(但可以通过类名调用类属性和类方法)。若要获得当前时间的字符串时,并不一定需要实例化对象。其实,我们也可以在类外面写一个同样的函数来做这些事,但是这样做就打乱了逻辑关系,也会导致以后代码维护困难。"
]
},
{
"cell_type": "markdown",
"id": "4e5d5b8e",
"metadata": {},
"source": [
"# 继承\n",
"\n",
"在程序中,继承的概念就要简单得多了,即描述的是事物之间的所属关系,例如猫和狗都属于动物,程序中便可以描述为猫和狗继承自动物;同理,波斯猫和巴厘猫都继承自猫,而沙皮狗和斑点狗都继承自狗。代码如下:"
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "02824342",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Xuanmao的名字为:玄猫\n",
"Xuanmao的颜色为:黑色\n",
"玄猫--在吃\n",
"小黑--在跑\n"
]
}
],
"source": [
"# 定义一个父类,如下: \n",
"class Cat( ): \n",
" def __init__(self, name, color ): \n",
" self.name = name \n",
" self.color = color \n",
" def run(self): \n",
" print(\"%s--在跑\" %self.name) \n",
"# 子类在继承的时候,在定义类时,小括号()中为父类的名字,继承Cat类如下: \n",
"class TianyuanCat(Cat): \n",
" def setNewName(self, newName): \n",
" self.name = newName \n",
" def eat(self): \n",
" print(\"%s--在吃\"%self.name) \n",
"Xuanmao = TianyuanCat(\"玄猫\", \"黑色\") \n",
"print('Xuanmao的名字为:%s' %Xuanmao.name) \n",
"print('Xuanmao的颜色为:%s' %Xuanmao.color) \n",
"Xuanmao.eat() \n",
"Xuanmao.setNewName('小黑') \n",
"Xuanmao.run() "
]
},
{
"cell_type": "markdown",
"id": "413d6bec",
"metadata": {},
"source": [
"虽然子类TianyuanCat没有定义初始化方法和run方法,但是父类(Cat)有,所以在子类继承父类的时候这个方法就被继承了,所以只要创建TianyuanCat的对象,就默认执行了那个继承过来的__init__()方法了。\n",
"\n",
"此外,还需要注意的是:\n",
"\n",
"(1)类的私有的属性,不能通过对象直接访问,但是可以通过方法访问。\n",
"\n",
"(2)私有的方法不能通过对象直接访问。\n",
"\n",
"(3)私有的属性、方法不会被子类继承,也不能被访问。\n",
"\n",
"(4)一般情况下,私有的属性、方法都是不对外公布的,往往用来做内部的事情,起到安全的作用。\n",
"代码如下:"
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "1e390d88",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"白色\n",
"动物\n",
"白色\n",
"------分割线-----\n",
"黄色\n",
"小花狗\n",
"黄色\n"
]
}
],
"source": [
"class Animal(object): \n",
" def __init__(self, name='动物', color='白色'): \n",
" self.__name = name \n",
" self.color = color \n",
" def __test(self): \n",
" print(self.__name) \n",
" print(self.color) \n",
" def test(self): \n",
" print(self.__name) \n",
" print(self.color) \n",
"class Dog(Animal): \n",
" def dogTest1(self): \n",
" #print(self.__name) #不能访问到父类的私有属性 \n",
" print(self.color) \n",
" def dogTest2(self): \n",
" #self.__test() #不能访问父类中的私有方法 \n",
" self.test() \n",
"A = Animal() \n",
"#print(A.__name) #程序出现异常,不能访问私有属性 \n",
"print(A.color) \n",
"#A.__test() #程序出现异常,不能访问私有方法 \n",
"A.test() \n",
"print(\"------分割线-----\") \n",
"D = Dog(name = \"小花狗\", color = \"黄色\") \n",
"D.dogTest1() \n",
"D.dogTest2() "
]
},
{
"cell_type": "markdown",
"id": "6b8cc594",
"metadata": {},
"source": [
"所谓多继承,即子类有多个父类,并且具有它们的特征,Python中的多继承代码如下:"
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "3435757c",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"----A----\n",
"----B----\n"
]
}
],
"source": [
"# 定义一个父类 \n",
"class A: \n",
" def printA(self): \n",
" print('----A----') \n",
"# 定义一个父类 \n",
"class B: \n",
" def printB(self): \n",
" print('----B----') \n",
"# 定义一个子类,继承自A、B \n",
"class C(A,B): \n",
" def printC(self): \n",
" print('----C----') \n",
"obj_C = C() \n",
"obj_C.printA() \n",
"obj_C.printB()"
]
},
{
"cell_type": "markdown",
"id": "45f0c4d6",
"metadata": {},
"source": [
"注意一点,如果在上面的多继承例子中,如果父类A和父类B中,有一个同名的方法,那么通过子类去调用的时候,调用先继承的父类方法。代码如下:"
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "77154f89",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"----B test----\n"
]
}
],
"source": [
"#第3章/lei.py\n",
"class base(object): \n",
" def test(self): \n",
" print('----base test----') \n",
"class A(base): \n",
" def test(self): \n",
" print('----A test----') \n",
"# 定义一个父类 \n",
"class B(base): \n",
" def test(self): \n",
" print('----B test----') \n",
"# 定义一个子类,继承自A、B \n",
"class C(B,A): \n",
" pass \n",
"obj_C = C() \n",
"obj_C.test() "
]
},
{
"cell_type": "markdown",
"id": "b84f7278",
"metadata": {},
"source": [
"调用与重写父类方法。所谓重写,就是子类中,有一个和父类相同名字的方法,在子类中的方法会覆盖掉父类中同名的方法,代码如下:"
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "27b9ce51",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"miao!~~!!!\n"
]
}
],
"source": [
"class Cat: \n",
" def sayHello(self): \n",
" print(\"miaomiao~~\") \n",
"class Xuanmao(Cat): \n",
" def sayHello(self): \n",
" print(\"miao!~~!!!\") \n",
"xiaohei = Xuanmao() \n",
"xiaohei.sayHello()"
]
},
{
"cell_type": "markdown",
"id": "3d35fdad",
"metadata": {},
"source": [
"在类的继承里面super()方法是很常用的,它解决了子类调用父类方法的一些问题,例如,当父类多次被调用时只执行一次,优化了执行逻辑,下面我们来详细看一下。\n",
"当存在继承关系的时候,有时候需要在子类中调用父类的方法,此时最简单的方法是把对象调用转换成类调用,需要注意的是这时self参数需要显式传递,代码如下:"
]
},
{
"cell_type": "code",
"execution_count": 21,
"id": "f6072c96",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"miaomiao~~\n"
]
}
],
"source": [
"class Cat: \n",
" def sayHello(self): \n",
" print(\"miaomiao~~\") \n",
"class Xuanmao(Cat): \n",
" def sayHello(self): \n",
" # 调用父类方法事,直接使用父类名称进行调用 \n",
" Cat.sayHello(self) \n",
"xiaohei = Xuanmao() \n",
"xiaohei.sayHello() "
]
},
{
"cell_type": "markdown",
"id": "c75d5c24",
"metadata": {},
"source": [
"这样做有一些缺点,比如说如果修改了父类名称,那么在子类中也会涉及修改,尤其是子类很多的情况,每个子类都需要进行修改。此外,Python是一种允许多继承的语言。在多继承情况下,如上所示的方法需要重复多次编写,这显得有些冗余。为了解决这些问题,Python引入了super()机制。代码如下:"
]
},
{
"cell_type": "code",
"execution_count": 22,
"id": "d398894c",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"miaomiao~~\n"
]
}
],
"source": [
"class Cat: \n",
" def sayHello(self): \n",
" print(\"miaomiao~~\") \n",
"class Xuanmao(Cat): \n",
" def sayHello(self): \n",
" # 使用super()代替父类类名,即便类名改变,这里的super也不需要修改。 \n",
" super().sayHello() \n",
"xiaohei = Xuanmao() \n",
"xiaohei.sayHello() "
]
},
{
"cell_type": "markdown",
"id": "4570555f",
"metadata": {},
"source": [
"一般而言,super()在继承中经常用来继承父类的初始化方法,如super().__init__()。"
]
},
{
"cell_type": "markdown",
"id": "93ab44ca",
"metadata": {},
"source": [
"# 多态\n",
"\n",
"多态指的是一类事物有多种形态,比如动物有多种形态:猫、狗、猪,代码如下:"
]
},
{
"cell_type": "code",
"execution_count": 23,
"id": "aa2b620e",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"喵喵喵\n",
"汪汪汪\n",
"哼哼哼\n"
]
}
],
"source": [
"class Animal: #同一类事物:动物 \n",
" def talk(self): \n",
" pass \n",
"class Cat(Animal): #动物的形态之一:猫 \n",
" def talk(self): \n",
" print('喵喵喵') \n",
"class Dog(Animal): #动物的形态之二:狗 \n",
" def talk(self): \n",
" print('汪汪汪') \n",
"class Pig(Animal): #动物的形态之三:猪 \n",
" def talk(self): \n",
" print('哼哼哼') \n",
"#实例化得到三个对象 \n",
"cat=Cat() \n",
"dog=Dog() \n",
"pig=Pig() \n",
"cat.talk() # 喵喵喵 \n",
"dog.talk() # 汪汪汪 \n",
"pig.talk() # 哼哼哼"
]
},
{
"cell_type": "markdown",
"id": "90de0a58",
"metadata": {},
"source": [
"多态性指的是可以在不用考虑对象具体类型的情况下而直接使用对象,这就需要在设计时,把对象的使用方法统一成一种。\n",
"\n",
"例如上述代码中cat、dog、pig都是动物,但凡是动物肯定有说话方法(talk),于是我们可以不用考虑它们三者具体是什么类型的动物,而直接使用talk()方法。更进一步,可以定义一个统一的接口来使用,代码如下:"
]
},
{
"cell_type": "code",
"execution_count": 24,
"id": "f7ea2dc5",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"喵喵喵\n",
"汪汪汪\n",
"哼哼哼\n"
]
}
],
"source": [
"class Cat(Animal): #动物的形态之一:猫 \n",
" def talk(self): \n",
" print('喵喵喵') \n",
"class Dog(Animal): #动物的形态之二:狗 \n",
" def talk(self): \n",
" print('汪汪汪') \n",
"class Pig(Animal): #动物的形态之三:猪 \n",
" def talk(self): \n",
" print('哼哼哼') \n",
"#实例化得到三个对象 \n",
"cat=Cat() \n",
"dog=Dog() \n",
"pig=Pig() \n",
"def Talk(animal): \n",
" animal.talk() \n",
"Talk(cat) # 喵喵喵 \n",
"Talk(dog) # 汪汪汪 \n",
"Talk(pig) # 哼哼哼 "
]
},
{
"cell_type": "markdown",
"id": "85dc8f3b",
"metadata": {},
"source": [
"Python中一切皆对象,本身就支持多态性。多态性的好处在于增强了程序的灵活性和可扩展性,比如通过继承动物类(animal)创建了一个新的类,实例化得到的对象obj,可以使用相同的方式使用obj.talk(),代码如下:"
]
},
{
"cell_type": "code",
"execution_count": 25,
"id": "ee6ffd09",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"嗷...\n"
]
}
],
"source": [
"class Animal: #同一类事物:动物 \n",
" def talk(self): \n",
" pass \n",
"class Wolf(Animal): #动物的另外一种形态:狼 \n",
" def talk(self): \n",
" print('嗷...') \n",
"wolf=Wolf() # 实例出一头狼 \n",
"wolf.talk() # 使用者根本无需关心wolf是什么类型而调用talk"
]
},
{
"cell_type": "markdown",
"id": "ca480c4d",
"metadata": {},
"source": [
"最后,可以通过关键字@abc.abstractmethod在父类引入抽象类的概念来硬性限定子类必须有某些方法名,代码如下:"
]
},
{
"cell_type": "code",
"execution_count": 26,
"id": "646a5009",
"metadata": {},
"outputs": [],
"source": [
"import abc \n",
"# 指定metaclass属性将类设置为抽象类,抽象类本身只是用来约束子类的,不能被实例化 \n",
"class Animal(metaclass=abc.ABCMeta): \n",
" @abc.abstractmethod # 该装饰器限制子类必须定义有一个名为talk的方法 \n",
" def talk(self): # 抽象方法中无需实现具体的功能 \n",
" pass \n",
"class Cat(Animal): # 但凡继承Animal的子类都必须遵循Animal规定的标准 \n",
" def talk(self):# 若子类中没有一个名为talk的方法则会抛出异常TypeError,无法实例化 \n",
" # TypeError: Can't instantiate abstract class Cat with abstract methods talk \n",
" pass \n",
"cat=Cat()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1548d32b",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.9"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment