面向对象编程(Object-oriented programming, OOP)是一种基于对象概念的编程范式,可包含属性(attribute)形式的数据以及方法(method)形式的代码。另一种对 OOP 的解释是构建来灵活的且可复用的模块或是库,就像 Numpy 和 Pandas。
Python 中的所有内容都是对象。就像字符串(string)和列表(list)都是 Python 中的对象。而类就是创建对象的蓝图。基于面向对象机制编写的代码易于维护,是因为它的模块化结构,同时程序也因为封装而更安全。
这篇教程讨论了面向对象编程中的类、对象、方法、多态和继承,以给你提供一个良好的面向对象编程的基础。
类
Python 现有的数据结构中就像整型还有字符串,都被设计为代表类似员工编号、员工姓名之类的东西。OOP 中的类就像是对象的蓝图,你还可以设计你自己的数据结构。举个例子,你可以创建一个 Employee 类来展现员工姓名和薪水的属性。
关键字calss
用以创建一个类,后面紧跟类型与冒号,类的主体部分起一新行并且缩进一个制表符。构造函数constructor
是当你创建对象时被默认调用的一个方法,你需要通过关键字init
来创建构造函数。在下面的例子当中,我创建了一个 Employee 类,与两个(class attributes)类属性 status 与 number_of_employee,同时两个(instance attributes)实例属性 employee_id 与 name,还有一个 give_info() 方法。
class Employee: #class attributes status = "active" number_of_employee = 0 def __init__(self, employee_id, name): self.employee_id = employee_id #instance attribute self.name = name #instance attribute Employee.number_of_employee += 1 #class method def give_info(self): print("Name:",self.name,"\nID:",self.employee_id)
在表达式
self.employee_id = employee_id
中第一个 employee_id 是一个(instance attributes)实例属性或称为变量,第二个 employee_id 是一个参数,变量 name 也是同样的道理。当你通过类创建一个对象的时候,你可以这样写,
emre = Employee("101", "Emre Kutlug")
对象
如前所述,类是创建对象的蓝图。一个类和对象的关系可以理解为动物和瑜伽熊的关系;瑜伽熊是一种动物,动物是一个抽象的概念,它以瑜伽熊和米老鼠的形式来实现。因此我们要先创建一个对象的类后才能使用它的方法和属性。
对象也被称为实例。因此,创建一个类的对象的过程也被成为实例化(instantiation)。在 Python 中创建类的对象,你必须在类名后紧跟圆括号。
我还可以使用type
方法来检查这个对象的类型。输入:
type(emre)
输出:
<class '__main__.Employee'>
你可以通过类的对象来访问类的(class attributes)类属性和(instance attributes)实例属性或者调用类的方法。为此,你必须写下对象名后跟点(.)操作符与你想访问的属性名或是方法名,看看下面的例子吧,输入:
>>> emre.status 'active' >>> emre.number_of_employee 1 >>> emre.employee_id '21' >>> emre.give_info() Name: Bob ID: 21
属性
你可以用内建函数dir()
来查看一个对象的所有属性和方法。Python 中有很多的内建的属性与方法,下面的例子将会展示 emre 对象中的所有属性与方法,其中前面带有双下划线的都是内嵌的。
>>> dir(emre) ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'employee_id', 'give_info', 'name', 'number_of_employee', 'status'] >>>
两种类型的属性当中,(class attributes)类属性的值将贯穿全部的对象,而(instance attributes)实例属性的值会因对象的不同而不同。实例属性可以定义在任何方法当中,而类属性定义在方法之外。实例属性通过self
关键字来引用,而类属性在方法中通过类名来引用。在下面的例子当中,我们先来看 number_of_employee 属性的值,再来创建一个同类的其他对象
>>> emre.number_of_employee 1 >>> emma = Employee("102", "Emma Stone") >>> emma.give_info() Name: Emma Stone ID: 102 >>>
当我们再在下面的例子当中输出 number_of_employee 的值的时候,将会是 2,因为这是类属性,两个对象都共享其值。
>>> emma.number_of_employee 2 >>>
方法
前面提到过了,你可以用方法来实现对象的功能。之前我都是用一个类的对象来调用它的方法,但是还有另一种方法 —— 静态方法(static method),可以直接同过类名来调用。静态方法只能访问类属性,下面的例子当中,我将为 employee 类添加一个静态方法
class Employee: #class attributes status = "active" number_of_employee = 0 def __init__(self, employee_id, name): self.employee_id = employee_id #instance attribute self.name = name #instance attribute Employee.number_of_employee += 1 #class method def give_info(self): print("Name:",self.name,"\nID:",self.employee_id) @staticmethod def get_class_objective(): message = "The objective of this Employee class is to organize employee information with more modular manner" print (message)
输入:
>>> Employee.get_class_objective() The objective of this Employee class is to organize employee information with more modular manner >>>
全局变量与局部变量
类的属性也被称为是变量,变量又分为两种类型:局部变量和全局变量。类中的局部变量是在方法中定义的并只能再方法中访问到,因此你不能在 get_class_objective() 方法之外访问到 message 变量。当你试图访问的时候,将会得到 AttributError,
全局变量不被定义在任何方法当中,因此你可以在类种的任何地方访问到它们。之前例子当中的 status 与 number_of_employee 都是全局变量。
封装
OOP 中有三个主要的概念:分装 Encapsulation、继承 Inheritance 与多态 Polymorphism。封装指的就是数据隐藏。OOP 中的一个类不应该直接就能访问到另一个类的数据,或者说数据的访问应该交由方法来控制着。你可以用私有变量和 property 来控制类数据的访问。在变量名前加上两个下划线便可定义私有变量,例如 __age 就是私有变量。
你可以为 age 属性创建一个 property 来符合这个逻辑,就像下面的代码一样。一个 property 有三个部分,你必须定义这个属性 —— 下例中为 age;接着你必须通过@property
装饰器(decorator)来为属性定义 property;最后你必须创建一个 property setter,下例当中为 @age.setter 装饰器。你可以设定员工的年龄都必须在 19 到 99 岁之间,如果有人想给 age 属性设定超过其范围的值,将会引发报错并无法创建。
class Employee: #class attributes status = "active" number_of_employee = 0 def __init__(self, employee_id, name, age): self.employee_id = employee_id #instance attribute self.name = name #instance attribute self.age = age #instance attribute Employee.number_of_employee += 1 # Creates model property @property def age(self): return self.__age # Create property setter @age.setter def age(self, age): if age < 18: raise Exception('An Employee\'s age cannot be lower than 18') elif age > 99: raise Exception('An Employee\'s age cannot be upper than 99') else: self.__age = age
继承
OOP 中的继承就很像真实世界当中的孩子继承了他父母的一些特征,再加上自身独特的特征。继承了其他类的类被称为子类,被继承的类则为父类。就像下面这样
# Create Class Manager that inherits Employee class Manager(Employee): def set_team_size(self, team_size): self.team_size = team_size
为了说明某类继承了一个类,你必须将其父类写在其类名后的圆括号之中。Manager 类可以访问到其父类 Employee 的全部属性与方法
>>> muge = Manager("104", "Muge Ozkan", 30) >>> muge.name 'Muge Ozkan' >>> muge.status 'active' >>> muge.get_class_objective() The objective of this Employee class is to organize employee information with more modular manner >>>
除了其父类的属性与方法之外,Manager 类还有他独有的 set_team_size() 方法,顺带一提,一个类可以有两个以上的父类或是子类。
多态
多态指的是一个对象可以有多种表现方式的能力,两种类型的多态分别是:方法重写和方法重载(method overriding and method overloading。)
方法重写
方法重写意味着在子类与父类当中有着同名方法,虽然其中的定义是不同的但他们都保留着相同的名字。如果你还记得我们在 Employee 类当中有一个 give_info() 的方法,我们便可以在 Manager 类当中进行重写,以显示经理类中的团大大小信息,
class Manager(Employee): team_size = 10 def set_team_size(self, team_size): self.team_size = team_size def give_info(self): print("Name:",self.name,"\nID:",self.employee_id,"\nTeam Size:",self.team_size)
接着
>>> muge = Manager("104", "Muge Ozkan", 30) >>> muge.give_info() Name: Muge Ozkan ID: 104 Team Size: 10 >>>
如你所见,当子类与父类都在调用同名方法时产生了不同的表现,是因为在子类中重写了父类的方法。
方法重载
你可以通过改变调用时参数的类型与个数来重载任何一个方法,这样这个方法将会有不同的表现。在下面的例子中,如果我们在调用 calculate_salary() 方法时只传一个参数,它将返回这个参数。然而如果我们传递两个参数,它将返回两数之和。
class Manager(Employee): team_size = 10 def set_team_size(self, team_size): self.team_size = team_size def give_info(self): print("Name:",self.name,"\nID:",self.employee_id,"\nTeam Size:",self.team_size) def calculate_salary(self, salary, bonus=None): if bonus is not None: salary += bonus return salary
接着
>>> muge = Manager("104", "Muge Ozkan", 30) >>> muge.calculate_salary(12345) 12345 >>> muge.calculate_salary(12345, 678) 13023 >>>
结论
本篇教程介绍了面向对象编程中的类、对象、属性和方法的概念,接着是封装、继承和多态,他们都是面向对象编程中的核心。OOP 是最常用的编程范例之一,因此大多数现代的编程语言像 Python 都支持面向对象编程。
想了解更多关于python的干货知识,请扫码下方二维码哦!