Python 中的面向对象编程

src:https://developer.ibm.com/tutorials/object-oriented-programming-in-python/

author:Emre Kutlug

翻译:yidongren

面向对象编程(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 都支持面向对象编程。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值