前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python指南:面向对象程序设计

Python指南:面向对象程序设计

作者头像
王强
发布2018-08-09 17:31:21
5350
发布2018-08-09 17:31:21
举报
文章被收录于专栏:Python爬虫实战

Python 是一种多泛型语言,它没有强制程序员使用某种特定的程序设计风格,而是允许程序员采用过程型、函数型或面向对象的程序设计风格,也可以是这些编程风格的有效组合。对于大多数程序而言,尤其对中等规模或大规模的程序,采用面向对象的程序设计风格提供了很多优势。

1、面向对象方法

接下来将基于使用程序对圆进行描述这一问题,来解释纯过程型程序设计方法存在的问题。用于描述一个圆所需要的最少数据包括圆心坐标(x, y)以及圆的半径,简单的方法是使用一个三元组对圆进行描述,比如:circle = (25, 80, 12)

如果只给出这个表示,那么我们可以理解为x = 25, y = 80, radius = 12, 也可以理解为x = 25, radius = 80, y = 12,二义性是其一个不足之处。另外一个不足在于,只能通过索引位置对其中的值进行存取,还是得提前知道元组中每个位置所代表的含义。

可以使用 namedtuple 解决获取元素顺序的问题(假设distance_from_origin函数已定义):

代码语言:javascript
复制
import collections

Circle = collections.namedtuple('Circle', 'x, y, radius')
circle = Circle(13, 84, 9)
# 假定 distance_from_origin 函数已经定义
distance = distance_from_origin(circle.x, circle.y)

虽然可以解决元素顺序的问题,但是如果半径给定的值为负值,这个数据是不合理的,假设会在 distance_from_origin 函数中进行检查,但这只有在调用的时候才能检查,并不能在创建 Circle 对象的时候进行验证,这就是纯过程型程序设计的弊端。

对象:我们之前见过的 dict、int、str 等数据类型其实是一个类,我们也可以称之为一个 对象。

对象中通常包含属性——方法是可调用的属性,其他属性则是数据。方法其实也是一个函数,只不过其第一个参数是调用该方法的实例本身(self)。在属性名前以两个下划线引导,Python就会阻止无心的访问,因此可以认为是私有的。

面向对象的优势之一是如果我们有一个类,就可以对其进行专用化,这意味着创建一个新类,新类继承原始类的所有属性(数据和方法),通常可以添加或替换原始类中的某些方法,或添加更多的实例变量。

2、自定义类

创建自定义类的两种语法:

代码语言:javascript
复制
class className:
    suit

class className(base_classes):
    suit

2.1 属性与方法

我们从简单的类 Point 开始,该类存放坐标 (x, y),定义于文件 Shape.py 中,其完整实现如下:

代码语言:javascript
复制
import math

class Point:
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y


    def distance_from_origin(self):
        return math.hypot(self.x, self.y)


    def __eq__(self, other):
        return self.x == other.x and self.y == other.y


    def __repr__(self):
        return "Point({0.x!r}, {0.y!r})".format(self)


    def __str__(self):
        return "({0.x!r}, {0.y!r})".format(self)

Point 类有两个数据属性(self.x和self.y),还包含5个方法(不包括继承来的方法),其中4个属于特殊方法。导入 Shape 模块后, Point 类就可以像其他类一样进行使用,其属性可以直接存取。

01.Point继承关系

对 Point 类的基本使用:

代码语言:javascript
复制
import Shape

a = Shape.Point()
repr(a) # returns: 'Point(0, 0)'

b = Shape.Point(3, 4)
str(b)    # returns: '(3, 4)' 
b.distance_from_origin()    # returns: 5.0

对方法进行调用时,Python 会自动提供第一个参数——这个参数是对对象自身的对象引用(在 C++ 与 Java 中称为 this)。我们必须在参数列表中包含这一参数,根据约定,这一参数称为self。所有的对象属性都必须由self进行限定。

创建一个对象,需要两个步骤:调用特殊方法 __new__() 来创建该对象,之后调用特殊方法 __init__() 对其进行初始化。

2.2 继承与多态

接下来我们新建一个继承自 Point 类的 Circle 类,添加一个额外的属性(radius)以及3个新方法,此外重新实现Point类的几个方法:

代码语言:javascript
复制
class Circle(Point):
    def __init__(self, radius, x=0, y=0):
        super().__init__(x, y)
        self.radius = radius


    def edge_distance_from_origin(self):
        return abs(self.distance_from_origin() - self.radius)


    def area(self):
        return math.pi * (self.radius ** 2)


    def circumference(self):
        return 2 * math.pi * self.radius


    def __eq__(self, other):
        return self.radius == other.radius and self.x == other.x and self.y == other.y


    def __repr__(self):
        return "Circle({0.radius!r}, {0.x!r}, {0.y!r})".format(self)


    def __str__(self):
        return repr(self)

Circle 类的继承关系如下:

02.Circle继承关系

在 __init__() 方法中,使用 super() 来调用基类的 __init__() 方法,从而创建并初始化 self.x 属性与 self.y 属性。下面给出两个使用实例:

代码语言:javascript
复制
p = Shape.Point(28, 45)
c = Shape.Circle(5, 28, 45)
p.distance_from_origin()    # returns: 53.0
p.distance_from_origin()    # returns: 53.0

多态意味着,给定类的任意对象在使用时都可以看做该类的任意某个基类的对象,这也是为什么在创建子类时只需要实现我们需要的附加方法,必须重新实现的也只是那些需要替代的方法。

2.3 使用特性进行属性存取控制

property() 修饰器函数是一个内置函数,至多接受4个参数:一个获取者函数,一个设置者函数,一个删除者函数以及一个 docstring

为将属性转换为可读/可写的特性,我们必须创建一个私有属性,其中实际上存放了数据并提供获取者方法与设置者方法。接下来我们对 Circle 类的 radius 属性进行验证。

代码语言:javascript
复制
class Circle(Point):
    def __init__(self, radius, x=0, y=0):
        super().__init__(x, y)
        self.radius = radius

    @property
    def radius(self):
        """ The circle's radius

        >>>circle = Circle(-2)
        Traceback(most recent call last):
        ...
        AssertionError:radius must be nonzero and non-negative
        >>>circle = Circle(4)
        >>>circle.radius = -1
        Traceback(most recent call last):
        ...
        AssertionError:radius must be nonzero and non-negative
        >>>circle.radius = 6
        """
        return self.__radius

    @radius.setter
    def radius(self, radius):
        assert radius > 0, "radius must be nonzero and non-negative"
        self.__radius = radius

    ...

测试:

代码语言:javascript
复制
import Shape
circle = Shape.Circle(-4)

执行之后会报错:

代码语言:javascript
复制
AssertionError: radius must be nonzero and non-negative

这样就会在创建 Circle 对象时进行验证了。

本文介绍了 Python 对面向对象程序设计的基础知识。展示了纯过程型程序设计的一些不足,并使用面向对象来克服这些不足。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-05-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 C与Python实战 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、面向对象方法
  • 2、自定义类
    • 2.1 属性与方法
      • 2.2 继承与多态
        • 2.3 使用特性进行属性存取控制
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档