前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python编程思想(23):类和对象

Python编程思想(23):类和对象

作者头像
蒙娜丽宁
发布2020-06-24 15:52:58
7330
发布2020-06-24 15:52:58
举报
文章被收录于专栏:极客起源极客起源

类是面向对象的核心组成部分,类是一种自定义类型,并且可以将类作为变量的类型、以及函数参数和返回值的类型。

1. 如何定义类

在面向对象的程序设计过程中有两个重要概念:类(class)和对象( object),对象也被称为实例(instance),其中类是拥有类似特性的对象的抽象,可以把类理解成某种概念,对象才是一个具体存在的实体。

从这个意义上看,日常所说的具体的人就是对象,而“人类”相当于类。

Python定义类的简单语法如下:

代码语言:javascript
复制
class类名:
    执行语句...
    零个到多个类变量...
    零个到多个方法...

类名只要是一个合法的标识符即可,但这仅仅满足的是 Python的语法要求。如果从程序的可读性方面来看,Python的类名最好是由一个或多个有意义的单词连缀而成的,每个单词首字母大写(大驼峰命名法),其他字母全部小写,单词与单词之间不要使用任何分隔符。

从上面定义来看,Python的类定义有点像函数定义,都是以冒号(:)作为类体的开始,以缩进的部分作为类体的。区别只是函数定义使用def关键字,而类定义则使用 class关键字。

Python的类定义由类头(指 class关键字和类名部分)和统一缩进的类体构成,在类体中最主要的两类成员就是变量和方法。如果不为类定义任何变量和方法,那么这个类就相当于一个空类。如果空类不需要其他可执行语句,则可使用pass语句作为占位符。例如,如下类定义是允许的。

代码语言:javascript
复制
class MyClass
    pass

通常来说,空类没有太大的实际意义。类中各成员之间的定义顺序没有任何影响。各成员之间可以相互调用。

成员变量属于类本身,用于定义该类本身所包含的状态数据。而实例变量则属于该类的对象,用于定义对象所包含的状态数据。方法则用于定义该类的对象的行为或功能实现。

Python是一门动态语言,因此它的类所包含的类变量可以动态增加或删除。程序在类体中为新变量赋值就是增加类变量,程序也可在任何地方为已有的类增加变量。程序可通过del语句删除已有类的类变量。

与类变量类似,Python对象的实例变量也可以动态增加或删除。只要对新实例变量赋值就是增加实例变量,因此程序可以在任何地方为已有的对象增加实例变量。程序可通过del语句删除已有对象的实例变量。

在类中定义的方法默认是实例方法,定义实例方法的方式与定义函数的方式基本相同,只是实例方法的第一个参数会被绑定到方法的调用者(该类的实例)上。因此实例方法至少应该定义一个参数,该参数通常会被命名为self。

注意:

实例方法的笫1个参数并不一定要叫self,其实完全可以叫任何参数名,只是约定俗成地把该参数命名为self,这样具有最好的可读性。

在实例方法中有一个特别的方法:init。这个方法被称为构造方法。构造方法用于构造该类的对象,Python通过调用构造方法返回该类的对象(创建Python对象时不需要使用new)。

构造方法是一个类创建对象是第1个要执行的方法,因此 ,Python还提供了一个功能。如果开发者没有为该类定义任何构造方法,那么 Python会自动为该类定义一个只包含一个self参数的构造方法。

下面程序将定义一个 Product类。

示例代码:Product.py

代码语言:javascript
复制
class Product :
    '我们编写的第1个Python类'
    # 下面定义了一个类变量
    type = 'Mobile'
    def __init__(self, name = 'iPhone12 Pro', price=8000):
        # 下面为Product对象增加2个实例变量
        self.name = name
        self.price = price
    # 下面定义了一个buy方法
    def buy(self, price):
        print(f"您已经花了{price}元购买了一部{self.name}手机")

上面的Product类代码定义了一个构造方法,该构造方法只是方法名比较特殊,该方法的第1个参数同样是self,被绑定到构造方法初始化的对象。与函数类似的是, Python也允许为类定义说明文档,该文档同样被放在类声明之后、类体之前,如上面程序中第1行的字符串所示。

在定义类之后,接下来即可使用该类了。Python的类大致有如下作用。

  • 定义变量
  • 创建对象
  • 派生子类

下面先介绍如何创建类的实例(对象),以及如何使用对象。

2. 对象的创建和使用

创建对象的根本途径是构造方法,调用某个类的构造方法即可创建这个类的对象, Python无须使用new调用构造方法。如下代码示范了调用 Product类的构造方法(Product类的代码见前面的内容)。

代码语言:javascript
复制
# 调用Product类的构造方法,返回一个product
# 将该Product对象赋值给product变量
product = Product()

在创建对象之后,接下来即可使用该对象了。Python的对象大致有如下作用。

  • 操作对象的实例变量(包括访问实例变量的值、添加实例变量、删除实例变量);
  • 调用对象的方法;

对象访问方法或变量的语法如下:

代码语言:javascript
复制
对象.变量|方法(参数)

在这种方式中,对象是主调者,用于访问该对象的变量或方法。

下面代码通过Product对象来调用 Product的实例和方法(Product类的源代码见前面的内容)。

代码语言:javascript
复制
# 输出product的name、price实例变量
print(product.name, product.price)
# 访问product的name实例变量,直接为该实例变量赋值
product.name = 'iPhone12 pro max'
product.price = 12000
# 调用product的buy()方法,声明buy()方法时定义了2个形参
# 但第1个形参(self)是自动绑定的,因此调用该方法只需为第2个形参指定一个值
product.buy(12000)
# 再次输出p的name、age实例变量
print(product.name, product.price)

上面程序开始访问了product对象的name和price两个实例变量。这两个变量是何时定义的呢?留意在Product的构造方法中有如下2行代码

代码语言:javascript
复制
self. name= name
self. price = price

这两行代码用传入的name和price参数(这两个参数都有默认值)对self的name、price变量赋值。由于 Python的第1个self参数是自动绑定的(在构造方法中自动绑定到该构造方法初始化的对象),而这2行代码就是对self的name、price两个变量赋值,也就是对该构造方法初始化的对象(product对象)的name和age变量赋值,即为product对象增加了name和price两个实例变量。

上面代码中通过Product对象调用了buy方法,在调用该方法时必须为方法的形参赋值。但buy方法的第1个形参self是自动绑定的,它会被绑定到方法的调用者(product)上,因此程序只需要为buy方法传入1个浮点数作为参数值,这个浮点数将被传给price参数。

大部分时候,定义一个类就是为了重复创建该类的对象,同一个类的多个对象具有相同的特征,而类则定义了多个对象的共同特征。从某个角度来看,类定义的是多个对象的特征,因此类不是个具体存在的实体,对象才是一个具体存在的实体。

3. 动态增加实例变量

由于 Python是动态语言,因此程序完全可以为product对象动态增加实例变量。只要为它的新变量赋值即可,动态删除实例变量需要使用del语句。

为Product对象增加一个size实例变量

代码语言:javascript
复制
# 为product对象增加一个size实例变量
product.size = '6.7英寸'
print(product.size)
# 删除product对象的name实例变量
del product.name
# 再次访问pproduct的name实例变量
# print(product.name)  # AttributeError

上面程序先为product对象动态增加了一个size实例变量。只要对roduct对象的 price实例变量赋值,就是新增一个实例变量。

接下来程序中调用del删除了product对象的name实例变量,当程序再次访问print( p name)时就会导致 Attribute error错误,并提示:" Product' object has no attribute name

Python是动态语言,当然也允许为对象动态增加方法。比如上面程序中在定义 Product类时只定义了一个buy方法,但程序完全可以为product对象动态增加方法。

但需要说明的是,为product对象动态增加的方法,Python不会自动将调用者自动绑定到第一个参数(即使将第1个参数命名为self也没用)。例如如下代码。

代码语言:javascript
复制
# 先定义一个函数
def info(self):
    print("---info函数---", self)
# 使用info对Product的info方法赋值(动态绑定方法)
product.info = info
# Python不会自动将调用者绑定到第一个参数,
# 因此程序需要手动将调用者绑定为第一个参数
product.info(product)

# 使用lambda表达式为product对象的bar方法赋值(动态绑定方法)
product.info = lambda self: print('--lambda表达式--', self)
product.info(product)

上面两行粗体字代码分别使用函数、 lambda表达式为p对象动态增加了方法,但对于动态增加的方法,Python不会自动将方法调用者绑定到它们的第1个参数,因此程序必须手动为第1个参数传入参数值,如上面程序中所示。

如果希望动态增加的方法也能自动绑定到第1个参数,则可借助于 types模块下的 Method Type进行包装。例如如下代码。

代码语言:javascript
复制
def intro_func(self, price):
    print(f"您已经花了{price}元购买了一部手机")
# 导入MethodType
from types import MethodType
# 使用MethodType对intro_func进行包装,将该函数的第一个参数绑定为p
product.intro = MethodType(intro_func, product)
# 第一个参数已经绑定了,无需传入
product.intro("生活在别处")

正如从上面代码所看到的,通过 Method Type包装 intro_func函数之后(包装时指定了将该函数的第1个参数绑定为product),为product对象动态增加的 info方法的第1个参数已经绑定,因此,程序通过product调用 intro方法时无须传入第一个参数的值。只需要像定义类时已经定义了 info方法一样。

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

本文分享自 极客起源 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档