摘要:本文介绍Python中的面向对象编程,包括类、对象、继承、多态等概念。通过本文,你可以掌握如何使用Python进行面向对象编程,并了解一些常见的面向对象设计模式。
1.Python面向对象基础
1.面对对象概念
面向对象编程(Object-Oriented Programming,简称OOP)是一种计算机编程思想,它将程序中的数据和行为看作一个整体,即“对象”,并通过封装、继承、多态等方式,实现对象之间的交互和复用。
在面向对象编程中,一个“类”代表了一个具有相似特征和行为的对象集合,包括其属性(数据成员)和方法(函数成员)。而“对象”则是该类的实例化结果,具有独立的状态和行为。同时,通过继承可以实现代码的复用,通过多态可以实现不同对象之间的灵活组合和调用。
面向对象编程的优点包括:程序结构清晰、可读性高、可维护性强、重用性好、易于扩展等。因此,在许多编程领域中,如软件开发、Web开发、游戏开发、大数据处理等,面向对象编程已经成为主流的编程范式之一。
简单来说一个对象的所有应该具有特征特性信息,都是由其所属的类来决定的,但是每个对象又可以具有不同的特征特性信息,比如,我自己(人类)这个对象,名字叫老王,性别男,会写代码,会教书;另一个对象(人类)可能叫赵薇,性别女,会演戏,会唱歌!
2.魔术方法
Python中的魔术方法(Magic Methods),也称特殊方法或双下划线方法,__xxx__()的函数叫做魔术方法,是在类定义中使用双下划线前缀和后缀命名的方法。这些魔术方法用于实现Python中的一些高级特性,例如运算符重载、属性访问控制、对象比较等。
以下是一些常见的Python魔术方法:
__init__(self, ...):构造函数,在创建对象时被调用
__new__(cls, ...):创建对象时调用的方法,返回一个新的对象
__repr__(self):打印对象时调用,返回一个描述对象的字符串
__str__(self):将对象转换为字符串时调用
__add__(self, other):定义加法运算符“+”的行为
__sub__(self, other):定义减法运算符“-”的行为
__eq__(self, other):定义相等运算符“==”的行为
__lt__(self, other):定义小于运算符“<”的行为
__gt__(self, other):定义大于运算符“>”的行为
__len__(self):定义获取长度的行为,通常用于容器类
1.__init__
()方法
__init__
方法用于在创建对象时对对象进行初始化操作。它在类实例化时自动调用,即通过类创建一个新的实例对象时,__init__
方法会被自动调用。
__init__方法有一个默认参数self,它表示类实例对象本身。除self参数外,__init__方法还可以接受其他参数,这些参数用于对类实例对象的属性进行初始化赋值操作。
例如,我们定义一个名为Person的类,将name和age作为实例变量传入__init__方法中:
class Person: def __init__(self, name, age): self.name = name self.age = age
当我们创建一个Person
类的实例对象时,__init__
方法会被自动调用,并且将name
和age
参数传递给它:
person = Person("Tom", 20)
在上面的代码中,我们创建了一个Person类的实例对象person,并将"Tom"和20作为name和age参数传递给了__init__方法。在__init__方法中,我们使用self.name = name和self.age = age对person对象的name和age属性进行了初始化赋值操作。
2.__str__
()方法
__str__
方法用于将对象转换为字符串形式。当我们使用print()
函数输出一个对象时,Python会自动调用该对象的__str__
方法,并返回一个字符串表示该对象。
因此,我们可以通过重写__str__
方法来自定义对象的字符串表示形式,以便更好地展示对象信息或方便调试。
下面是一个例子,我们定义了一个名为Person
的类,它有name
和age
两个属性,同时还重写了__str__
方法:
class Person: def __init__(self, name, age): self.name = name self.age = age def __str__(self): return f"{self.name} is {self.age} years old."
在上面的代码中,我们通过return
语句返回一个包含name
和age
属性的字符串,这样当我们使用print()
函数输出Person
对象时,就会返回类似于"Tom is 20 years old."
的字符串。
现在,让我们创建一个Person
对象并输出它的字符串表示形式:
person = Person("Tom", 20) print(person)
运行上面的代码,输出结果如下:
Tom is 20 years old. • 1
可以看到,在使用print()
函数输出person
对象时,Python会自动调用Person
类的__str__
方法,并返回一个包含name
和age
属性的字符串。
3.__del__()
方法
__del__()
方法用于在对象被销毁时进行一些清理工作。它在对象被垃圾回收器回收前被自动调用。
当一个对象不再被引用,或者它的引用计数为0时,Python的垃圾回收机制会自动回收该对象。在对象被回收之前,如果它定义了__del__()方法,该方法就会被调用,来完成对象的清理工作。
例如,我们定义一个名为Person的类,并在其中定义了__del__()方法,用于在对象被销毁时打印一条消息:
class Person: def __init__(self, name): self.name = name def __del__(self): print(f"{self.name} has been deleted.")
当我们创建一个Person
类的实例对象并将它赋值给person
变量时,该对象的引用计数为1:
person = Person("Tom")
当我们将person
变量重新赋值为None
时,该对象的引用计数为0,即没有任何引用指向该对象,因此对象会被垃圾回收器回收,并自动调用__del__()
方法:
person = None
在上面的代码中,我们将person变量重新赋值为None,这意味着对该对象的唯一引用已经被删除。此时,Python的垃圾回收机制会自动将该对象回收,并调用__del__()方法来完成清理工作。在本例中,__del__()方法会打印一条消息:Tom has been deleted.
__del__()方法可以用于实现一些清理工作,例如关闭文件、释放资源等。虽然这种方式不如显式地调用close()或者release()方法安全可靠,但是当我们忘记或无法及时调用这些方法时,__del__()方法可以作为一个备选方案来确保程序的正确性。
3.面向对象基础案例
1.学员成绩案例
需求:定义学员信息类,包含姓名、成绩属性,定义成绩打印方法(90分及以上显示优秀,80分及以上显示良好,70分及以上显示中等,60分及以上显示合格,60分以下显示不及格)
class Student(): def __init__(self, name, score): self.name = name self.score = score def print_grade(self): if self.score >= 90: print(f'{self.name},{self.score}分,成绩优秀') elif self.score >= 80: print(f'{self.name},{self.score}分,成绩良好') elif self.score >= 70: print(f'{self.name},{self.score}分,成绩中等') elif self.score >= 60: print(f'{self.name},{self.score}分,成绩及格') p1 = Student('Tom', 80) p1.print_grade() p2 = Student('Rose', 99) p2.print_grade()
2.动物园系统案例
实现一个简单的动物园系统,其中不同类型的动物都可以被创建并带有特定于其类型的方法
class Animal: def __init__(self, name, species): self.name = name self.species = species def eat(self): print(f"{self.name} is eating.") class Lion(Animal): def __init__(self, name): super().__init__(name, "lion") def roar(self): print("Roar!") class Elephant(Animal): def __init__(self, name): super().__init__(name, "elephant") def trumpet(self): print("Trumpet!") class Giraffe(Animal): def __init__(self, name): super().__init__(name, "giraffe") def stretch(self): print("Stretch!")
在上述代码中,我们创建了一个Animal类,并在该类中添加了一个eat()
方法。然后,我们从Animal类派生出Lion、Elephant和Giraffe类,并为每个类添加特定于该类的方法。
现在,我们可以创建不同类型的动物对象并调用它们的方法:
lion = Lion("Simba") elephant = Elephant("Dumbo") giraffe = Giraffe("Melman") lion.eat() lion.roar() elephant.eat() elephant.trumpet() giraffe.eat() giraffe.stretch()
3.小志爱跑步案例
需求:小志体重75.0公斤,小志每次跑步会减掉0.50公斤,小志每次吃东西体重增加1公斤分析:① 对象:小志② 属性:姓名、体重③ 方法:跑步、吃东西
class Person(): def __init__(self, name, weight): self.name = name self.weight = weight def __str__(self): return f'姓名:{self.name},体重:{self.weight}' def run(self): print(f'{self.name}爱跑步,每次跑步可以减掉0.5公斤') self.weight -= 0.5 def eat(self): print(f'{self.name}是一个小吃货,每次吃东西体重都会增长1公斤,他太难了') self.weight += 1 xm = Person('小志',75.0) print(xm)
2.Python面向对象高级
Python中的面向对象编程是一种非常强大的编程范式,它允许你将代码组织成可重用和可扩展的模块。
面向对象的三大特性:封装、继承、多态。
① 封装
将属性和方法书写到类的里面的操作即为封装,封装可以为属性和方法添加私有权限。
② 继承
子类默认继承父类的所有属性和方法,与此同时子类也可以重写父类属性和方法。
③ 多态
多态是同一类事物具有的多种形态。不同的对象调用同一个接口(方法),表现出不同的状态,称为多态。
1.Python中的继承
1.什么是继承
生活中的继承,一般指的是子女继承父辈的财产。
Python继承是面向对象编程中的一个重要概念,它允许一个类(子类)继承另一个类(父类)的特性和方法,同时可以增加或覆盖父类的功能。通过继承,子类可以重用父类的代码,并且可以在不破坏原有结构的情况下进行扩展和修改。
在Python中,子类可以继承父类的属性和方法,也可以重写父类的方法或者添加新的方法。通过继承,子类可以获得父类的方法和属性,从而避免了重复编写代码的问题,并且可以更方便地管理和维护程序代码。
2.继承的基本语法
基本语法:
# 父类B class B(object): pass # 子类A class A(B): pass
在Python中,所有类默认继承object类,object类是顶级类或基类;其他子类叫做派生类。
3.与继承相关的几个概念
继承:一个类从另一个已有的类获得其成员的相关特性,就叫作继承!
派生:从一个已有的类产生一个新的类,称为派生!
很显然,继承和派生其实就是从不同的方向来描述的相同的概念而已,本质上是一样的!
父类:也叫作基类,就是指已有被继承的类!
子类:也叫作派生类或扩展类
扩展:在子类中增加一些自己特有的特性,就叫作扩展,没有扩展,继承也就没有意义了!
单继承:一个类只能继承自一个其他的类,不能继承多个类,单继承也是大多数面向对象语言的特性!
多继承:一个类同时继承了多个父类, (C++、Python等语言都支持多继承)
4.Python中的单继承
单继承:一个类只能继承自一个其他的类,不能继承多个类。这个类会有具有父类的属性和方法。
基本语法:
# 父类B class B(object): pass # 子类A class A(B): pass
案例:猫,狗 都属于动物,它们行为相似性高。都会吃、会睡、会叫
class Animal(object): def eat(self): print('吃...') def sleep(self): print('睡...') def call(self): print('叫...') class Dog(Animal): pass class Cat(Animal): pass
5.重写父类属性和方法
重写也叫作覆盖,就是当子类成员与父类成员名字相同的时候,从父类继承下来的成员会重新定义!
此时,通过子类实例化出来的对象访问相关成员的时候,真正其作用的是子类中定义的成员!
上面单继承例子中 Animal 的子类 Cat和Dog 继承了父类的属性和方法,但是我们狗类Dog 有自己的叫声’汪汪叫’,猫类 Cat 有自己的叫声 ‘喵喵叫’ ,这时我们需要对父类的 call() 方法进行重构。如下:
案例:猫,狗 都属于动物,它们行为相似性高。都会吃、会睡、会叫
class Animal(object): # 定义属性name和age def __init__(self, name, age): self.name = name self.age = age def eat(self): print('吃...') def sleep(self): print('睡...') def call(self): print('叫...') class Dog(Animal): def call(self): print('汪汪叫...') class Cat(Animal): def call(self): print('喵喵叫...') # Dog实例对象 dog = Dog() dog.eat() dog.call() # Cat实例对象 cat = Cat() cat.eat() cat.call()
6.调用父类属性和方法
super():调用父类属性或方法,完整写法:super(当前类名, self).属性或方法()
class Animal(): def __init__(self, name, age): self.name = name self.age = age def eat(self): print('吃...') def sleep(self): print('睡...') def call(self): print('叫...') class Dog(Animal): def __init__(self, name, age, sex): super().__init__(name, age) self.sex = sex def __str__(self): return f'{self.name},今年{self.age}岁了,我会汪汪叫...' class Cat(Animal): def __init__(self, name, age, sex): super().__init__(name, age) self.sex = sex def __str__(self): return f'{self.name},今年{self.age}岁了,我会喵喵叫...'
7.Python中的多继承实现
多继承:一个类同时继承了多个父类,并且同时具有所有父类的属性和方法例如:孩子会继承父亲 和 母亲的方法。
class Father(object): pass class Mother(object): pass class Child(Father, Mother): pass
注:MRO(Method Resolution Order):方法解析顺序,我们可以通过类名.__mro__或类名.mro()获得“类的层次结构”,方法解析顺序也是按照这个“类的层次结构”寻找到。
2.Python中的多态
1.什么是多态
多态指的是一类事物有多种形态。
定义:多态是一种使用对象的方式,子类重写父类方法,调用不同子类对象的相同父类方法,可以产生不同的执行结果
① 多态依赖继承
② 子类方法必须要重写父类方法
好处:调用灵活,有了多态,更容易编写出通用的代码,做出通用的编程,以适应需求的不断变化!
2.多态的实现步骤
1.定义父类,并提供公共方法
2.定义子类,并重写父类方法
3.传递子类对象给调用者,可以看到不同子类执行效果不同
3.多态实现
类具有继承关系,并且子类类型可以向上转型看做父类类型,如果我们从 Animal 派生出 Cat和 Dog,并都写了一个 call() 方法,如下示例:
class Animal(object): def __init__(self, name, age): self.name = name self.age = age def call(self): print(self.name, '会叫') class Cat(Animal): def __init__(self, name, age, sex): super(Cat, self).__init__(name, age) self.sex = sex def call(self): print(self.name, '会“喵喵”叫') class Dog(Animal): def __init__(self, name, age, sex): super(Dog, self).__init__(name, age) self.sex = sex def call(self): print(self.name, '会“汪汪”叫') def do(all): all.call() A = Animal('小黑',4) C = Cat('喵喵', 2, '男') D = Dog('旺财', 5, '女') for x in (A,C,D): do(x) #小黑 会叫 #喵喵 会“喵喵”叫 #旺财 会“汪汪”叫
**这种行为称为多态。也就是说,方法调用将作用在 all 的实际类型上。**C 是 Cat 类型,它实际上拥有自己的 call() 方法以及从 Animal 继承的 call 方法,但调用 C .call() 总是先查找它自身的定义,如果没有定义,则顺着继承链向上查找,直到在某个父类中找到为止。传递给函数 do(all) 的参数 all 不一定是 Animal 或 Animal 的子类型。任何数据类型的实例都可以,只要它有一个 call() 的方法即可。其他类不继承于 Animal,具备 call 方法也可以使用 do 函数。
这就是动态语言,动态语言调用实例方法,不检查类型,只要方法存在,参数正确,就可以调用。
3.面向对象的其他特性
1.私有属性和私有方法
在Python中,可以为实例属性和方法设置私有权限,即设置某个实例属性或实例方法不继承给子类。
设置私有属性和私有方法的方式非常简单:在属性名和方法名 前面 加上两个下划线 “__” 即可。
案例1:私有属性和私有方法设置方式
class Girl(): def __init__(self): self.name = '小美' self.__age = 18 def __showinfo1(self): print('姓名:%s,年龄:%d' % (self.name, self.__age)) def showinfo2(self): print('姓名:%s,年龄:%d' % (self.name, self.__age)) girl = Girl() print(girl.name)#小美 girl.showinfo2()#姓名:小美,年龄:18 # 外界不能直接访问私有属性和私有方法 print(girl.__age)#'Girl' object has no attribute '__age' girl.__showinfo1()#'Girl' object has no attribute '__showinfo1'
2.私有属性和私有方法与继承的关系
案例2:私有属性和私有方法也不能被子类继承
class Cat(object): def __init__(self): self.name = '猫大师' self.kungfu = '闪、展、腾、挪' self.__skill = '爬树' class Tiger(Cat): pass tiger = Tiger() tiger.name = '学徒虎' print(f'{tiger.name},功夫:{tiger.kungfu}、{tiger.__skill}') #'Tiger' object has no attribute '__skill'
3.获取与设置私有属性值
在Python中,一般定义函数名’ get_xx ‘用来获取私有属性,定义’ set_xx '用来修改私有属性值。
class Girl(): def __init__(self): self.name = '小美' self.__age = 18 def get_age(self): return self.__age def set_age(self, age): self.__age = age girl = Girl() girl.set_age(24) print(girl.get_age()) #24
4.类属性和实例属性
类属性就是 类对象中定义的属性,它被该类的所有实例对象所共有。通常用来记录 与这类相关 的特征,类属性 不会用于记录 具体对象的特征。
案例:定义一个工具类, 每件工具都有自己的名称,需求:知道使用这个类,创建了多少个工具对象?
class Tool(object): # 定义类属性,用于记录创建了多少个工具对象 count = 0 def __init__(self, name): self.name = name # 针对类属性做一个计数+1操作 Tool.count += 1 tool1 = Tool('斧头') tool2 = Tool('榔头') tool3 = Tool('铁锹') # 输出工具对象的总数 print(Tool.count) #3
5.类方法
类方法就是针对类对象定义的方法,在类方法中可以直接访问类属性或者调用其他类方法。
基本语法:
@classmethod def 类名称(cls): pass
类方法需要用修饰器" @classmethod “来标识,告诉解释器这是一个类方法类方法的第一个参数应该是” cls "
① 有哪一个类调用的方法,方法内的" cls “就是哪一个类的引用
② 这个参数和示例方法的第一个参数是"self"类似
③ 提示使用其他名称也可以,不过习惯使用” cls " 通过类名.调用类方法,调用方法时,不需要传递" cls "参数
在方法内部
① 可以通过"cls."访问类的属性
② 也可以通过 “cls.” 调用其他的类方法
6.静态方法
在开发时,如果需要在类中封装一个方法,这个方法:
① 既 不需要访问实例属性或者调用实例方法
② 也 不需要访问类属性或者调用类方法
这个时候,可以把这个方法封装成一个静态方法
基本语法:
@staticmethod def 静态方法名(): pass
静态方法需要用修饰器 “@staticmethod” 来标识,告诉解释器这是一个静态方法。
通过类名.调用 静态方法
案例:使用静态方法创建一个游戏菜单。
class Game: @staticmethod def menu(): print('------') print('开始[1]') print('暂停[2]') print('退出[3]') Game.menu()
4.综合案例
1.需求分析:
要求:设计一个Game类
属性:
定义一个类属性top_score记录游戏的历史最高分
定义一个实例属性player_name记录当前游戏的玩家姓名
方法:
静态方法show_help显示游戏帮助信息
类方法show_top_score显示历史最高分
实例方法start_game开始当前玩家的游戏
2.主程序步骤
① 查看帮助信息
② 查看历史最高分
③ 创建游戏对象,开始游戏
class Game(object): top_score = 0 def __init__(self, player_name): self.player_name = player_name @staticmethod def show_help(): print('游戏帮助信息') @classmethod def show_top_score(cls): print(f'历史最高分:{cls.top_score}') def start_game(self): print(f'游戏开始,{self.player_name}') # 1、查看游戏帮助 Game.show_help() # 2、显示历史最高分 Game.show_top_score() # 3、开始游戏 game = Game('heima') game.start_game()
3.总结
Python是一门支持面向对象编程的语言,它提供了丰富的工具和语法来实现面向对象程序设计。以下是Python面向对象编程的总结:
- 类和实例:使用class关键字定义一个类,类包含属性和方法,使用类创建实例对象。
- 属性和方法:属性是对象的状态,方法是对象的行为。类可以有多个属性和方法,实例对象可以访问这些属性和方法。
- 构造函数:构造函数是一个特殊的方法,在创建对象时被调用,用于初始化对象的属性。
- 继承:子类可以继承父类的属性和方法,通过继承可以实现代码重用和抽象化。
- 多态:不同的对象可以共享相同的接口,从而增强程序的灵活性和可扩展性。
- 封装:封装是面向对象编程的基本原则之一,它将数据和操作集成在一起,对外部隐藏实现细节。
- 访问权限:Python没有提供严格的访问控制机制,但可以使用命名约定和属性装饰器来模拟私有属性和方法的访问控制。
- 魔术方法:魔术方法是Python中的特殊方法,它们包含双下划线开头和结尾的名称。常见的魔术方法包括__init__、__str__、__repr__等。
总的来说,Python是一门优秀的面向对象编程语言,它提供了丰富的工具和语法来支持面向对象程序设计。掌握面向对象编程的基本概念和技巧,可以帮助我们开发更加灵活、可扩展和可维护的程序。