Python面向对象编程入门
课程目标:了解面向对象并可以根据面向对象知识进行编写代码。
课程概要:
- 初识面向对象
- 三大特性(面向对象)
- 封装
- 继承
- 多态
面向对象
面向对象编程是最有效的软件编写方法之一。
在面向对象编程中,你编写表示现实世界中的事物和情景的类,并基于这些类来创建对象。
编写类时,你定义一大类对象都有的通用行为。
基于类创建对象时,每个对象都自动具备这种通用行为,然后可根据需要赋予每个对象独特的个性。
使用面向对象编程可模拟现实情景,其逼真程度达到了令人惊讶的地步。根据类来创建对象称为实例化,这让你能够使用类的实例。
为什么学习面向对象
理解面向对象编程有助于你像程序员那样看世界,还可以帮助你真正明白自己编写的代码:不仅是各行代码的作用,还有代码背后更宏大的概念。了解类背后的概念可培养逻辑思维,让你能够通过编写程序来解决遇到的几乎任何问题。
随着面临的挑战日益严峻,类还能让你以及与你合作的其他程序员的生活更轻松。如果你与其他程序员基于同样的逻辑来编写代码,你们就能明白对方所做的工作。你编写的程序将能被众多合作者所理解,每个人都能事半功倍。
创建和使用类
想要通过面向对象去实现某个或某些功能时需要2步:
-
定义类,在类中定义方法,在方法中去实现具体的功能。
-
实例化类并的个一个对象,通过对象去调用并执行方法。
使用类几乎可以模拟任何东西。下面来编写一个表示小狗的简单类Dog,它表示的不是特定的小狗,而是任何小狗。对于大多数宠物狗,我们都知道些什么呢?它们都有名字和年龄。我们还知道,大多数小狗还会蹲下和打滚。由于大多数小狗都具备上述两项信息(名字和年龄)和两种行为(蹲下和打滚),我们的Dog类将包含它们。这个类让Python知道如何创建表示小狗的对象。编写这个类后,我们将使用它来创建表示特定小狗的实例。
创建Dog类
根据Dog类创建的每个实例都将存储名字和年龄,我们赋予了每条小狗蹲下(sit())和打滚(roll_over())的能力:
# dog.py
class Dog:
"""一次模拟小狗的简单尝试"""
def __init__(self, name, age) -> None:
"""初始化属性name和age"""
self.name = name
self.age = age
def sit(self):
"""模拟小狗收到命令时蹲下"""
print(f"{self.name} is now sitting.")
def roll_over(self):
"""模拟小狗收到命令打滚。"""
print(f"{self.name} rolled over!")
dog = Dog('小黑', 1)
dog.roll_over()
dog.sit()
---------------------------------------------------------
小黑 rolled over!
小黑 is now sitting.
---------------------------------------------------------
在Python中,首字母大写的名称指的是类。这个类定义中没有圆括号,因为要从空白创建这个类。编写了一个文档字符串,对这个类的功能做了描述。
方法__init__()
类中的函数称为方法。
__init__()
是一个特殊方法,每当你根据Dog类创建新实例时,Python都会自动运行它。
在每个类中都可以定义个特殊的:__init__ 初始化方法
,在实例化类创建对象时自动执行,即:对象=类()
。
在这个方法的名称中,开头和末尾各有两个下划线,这是一种约定,旨在避免Python默认方法与普通方法发生名称冲突。
在这个方法的定义中,形参self必不可少,而且必须位于其他形参的前面。
为何必须在方法定义中包含形参self呢?因为Python调用这个方法来创建Dog实例时,将自动传入实参self。每个与实例相关联的方法调用都自动传递实参self,它是一个指向实例本身的引用,让实例能够访问类中的属性和方法。
创建Dog实例时,Python将调用Dog类的方法__init__()
。我们将通过实参向Dog()传递名字和年龄,self会自动传递,因此不需要传递它。
self.name= name
获取与形参name相关联的值,并将其赋给变量name,然后该变量被关联到当前创建的实例。self.age = age
的作用与此类似。像这样可通过实例访问的变量称为属性。
Dog类还定义了另外两个方法:sit()
和roll_over()
。这些方法执行时不需要额外的信息,因此它们只有一个形参self。我们随后将创建的实例能够访问这些方法,换句话说,它们都会蹲下和打滚。
对象和self
- 对象,让我们可以在它的内部先封装一部分数据,以后想要使用时,再去里面获取。
- self,类中的方法需要由这个类的对象来触发并执行( 对象.方法名 ),且在执行时会自动将对象当做参数传递给self,以供方法中获取对象中已封装的值。
三大特性
面向对象编程在很多语言中都存在,这种编程方式有三大特性:封装、继承、多态。
封装
封装主要体现在两个方面:
- 将同一类方法封装到了一个类中,例如上述示例中:匪徒的相关方法都写在Terrorist类中;警察的相关方法都写在Police类中。
- 将数据封装到了对象中,在实例化一个对象时,可以通过
__init__
初始化方法在对象中封装一些数据,便于以后使用。
继承
传统的理念中有:儿子可以继承父亲的财产。
在面向对象中也有这样的理念,即:子类可以继承父类中的方法和类变量(不是拷贝一份,父类的还是属于父类,子类可以继承而已)。
父类
子类
基类
派生类
class Base:
def func(self):
print("Base.func")
class Son(Base):
def show(self):
print("Son.show")
s1 = Son()
s1.show()
s1.func() # 优先在自己的类中找,自己没有才去父类。
s2 = Base()
s2.func()
class Base:
def f1(self):
pass
class Foo(Base):
def f2(self):
pass
class Bar(Base):
def f3(self):
pass
o1 = Foo()
o1.f2()
o1.f1()
多态
多态,按字面翻译其实就是多种形态。
在程序设计中,鸭子类型(duck typing)是动态类型的一种风格。在鸭子类型中,关注点在于对象的行为,能作什么;而不是关注对象所属的类型,例如:一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟可以被称为鸭子。
在python中,不同的对象调用同一个接口,表现出不同的状态,称为多态。
class Duck():
def who(self):
print("I am a duck")
class Dog():
def who(self):
print("I am a dog")
class Cat():
def who(self):
print("I am a cat")
if __name__ == "__main__":
duck=Duck()
dog=Dog()
cat=Cat()
duck.who()
dog.who()
cat.who()
输出结果:
I am a duck
I am a dog
I am a cat
以上代码是多态吗?虽然不同的对象调用同一个接口表现出不同状态,但是!!这不是多态!要实现多态有两个前提:
1.继承:多态必须发生在父类与子类之间
2.重写:子类重写父类方法
把以上代码进行修改,使之继承父类,才实现多态,如下:
class Animal():
def who(self):
print("I am an Animal")
class Duck(Animal):
def who(self):
print("I am a duck")
class Dog(Animal):
def who(self):
print("I am a dog")
class Cat(Animal):
def who(self):
print("I am a cat")
if __name__ == "__main__":
duck=Duck()
dog=Dog()
cat=Cat()
duck.who()
dog.who()
cat.who()
多态有什么用?
其实以上代码实际貌似没啥用,为什么一定要去定义通过同一个函数名?我定义who1,who2,who3不可以吗?代码量也一样啊!
其实真正体验出多态好处的代码一般是这样写的
class Animal():
def who(self):
print("I am an Animal")
class Duck(Animal):
def who(self):
print("I am a duck")
class Dog(Animal):
def who(self):
print("I am a dog")
class Cat(Animal):
def who(self):
print("I am a cat")
def func(obj):
obj.who()
if __name__ == "__main__":
duck=Duck()
dog=Dog()
cat=Cat()
func(duck)
func(dog)
func(cat)
仅仅需要一个函数,就可以把不同对象的who函数表现出来了。这就增加了程序的灵活性,以不变应万变,不管你类中的who()写得如何得天花乱坠,我都可以用一个统一的形式来调用。另外,它增加了程序的可扩展性,不管是我们或者调用者,如果想增加新的功能,都不需要修改原来的代码。
例如,我想新增一个bird类,仅需增加以下代码,无需修改原来的。
class Bird(Animal):
def who(self):
print("I am a bird")
对于调用者,也仅需增加以下代码,无需修改原来的。
bird=Bird()
func(bird)
所以说多态有什么用?一是增加程序的灵活性,二是增加程序的可扩展性