资讯专栏INFORMATION COLUMN

python基础教程:类

fish / 2007人阅读

摘要:类对象被创建时存在于类命名空间内的所有名称都是有效的属性名称。类的实例化,是使用函数表示法,可以把类对象看做是会返回一个新的类实例的函数。这就是可变对象作为类变量时的特性。类变量是所有类的实例共享的属性和方法,实例变量是每个实例独有的数据。

Python是面向对象的高级编程语言,在Python里面“一切都是对象”:数字、字符串、元组、列表、字典、集合等内置数据类型,以及函数、方法、类、模块都是对象。

语言本身提供了上述的基本对象,但在实际编程中,我们要创造各种各样的对象,Python就为我们提供了创造我们自己的对象的方法:

类(Class),就是组合数据和功能的方法,它让我们创建一个新类型的对象,并可以创建该类型的新实例。类组合的数据,就是保存自己状态的属性,而它组合的功能(函数)就是改变自己状态的(定义在类中的)方法。类内部定义的函数,称为类的方法

Python中的类和其它语言(比如C++)有很多相似的特征但也有些区别。如果你已了解其它语言的类的概念,可以在学习Python类时做一定的对比进行学习;如果你没有学过其它语言也不要紧,学过之后你会发现,类的概念是如此简单。

类的定义

类的定义是通过关键字class实现的,下面是最简单的类的定义的样子:

class ClassName:
    语句1
    ...
    语句n

是不是这个形式跟函数的定义(def 语句)很像。因为类是数据和功能的组合,所以语句1可能是内部变量(数据)的定义和赋值语句,也可能是内部方法(函数)的定义语句。类内部的函数定义通常具有一种特别形式的参数列表,这是方法调用的约定规范里面指明的。这个特别形式就是第一个参数必须是self,后面将详细介绍。

进入类定义时,就会创建一个新的命名空间,并把它用作局部作用域。因此,所有对局部变量的赋值都是在这个新命名空间内进行的。特别的,函数定义会绑定到这个局部作用域里的新函数名称。

正常离开(从结尾出)类定义时,就会创建一个类对象。它基本上是一个包围在类定义所创建的命名空间内容周围的包装器。元素的(在进入类定义之前起作用的)局部作用域将重新生效,类对象将在这里被绑定到类定义头给出的类名称(在上面的例子中就是ClassName)。

类对象

类对象(比如上面例子的ClassName)支持两种操作:属性引用实例化

属性引用的语法跟Python中所有属性引用的方法一样:obj.name。类对象被创建时存在于类命名空间内的所有名称都是有效的属性名称。下面是一个包含数据和方法的简单的类定义:

class YuanRenXue:
    """A demo of class"""
    name = "猿人学"
    def say_hi(self):
        print("Hello world!")

对这个类的有效的属性引用就是:YuanRenXue.nameYuanRenXue.say_hi,它们分别返回一个字符串和一个函数对象。

类属性也可以被赋值,因此可以通过赋值来更改Yuanrenxue.name的值。

类的__doc__也是一个有效的属性,对他的引用会返回所属类的文档字符串:"A demo of class"

类的实例化,是使用函数表示法,可以把类对象看做是会返回一个新的类实例的函数。比如上面类对象的实例化就是:

yrx = YuanRenXue()

这就创建了一个类的新实例并将词对象分配给局部变量yrx

实例化操作可以看成是“调用”类对象。但我们在创建类实例时都想要做些初始化操作,为此类定义时可以定义一个名为__init__()的特殊方法。它是类实例化的初始化方法,跟C++语言中 的构造函数类似。

def __init__(self):
    self.data = None

定义了__init__()方法后,类的实例化操作会自动调用该方法。

当然,__init__()方法也可以有额外(除self之外)的参数以实现更灵活的初始化操作。类对象实例化时(“调用”类对象)传递的参数会被传递给__init__()方法。例如:

In [27]: class Point: 
    ...:     def __init__(self, x, y): 
    ...:         self.x = x 
    ...:         self.y = y 
    ...:

In [28]: p = Point(7, 8)

In [29]: p.x, p.y
Out[29]: (7, 8)
实例对象

类实例化后我们就得到了实例对象,对它的操作就是:属性引用。这里的有效属性名称是数据属性和方法。
o

数据属性,对应C++中的“数据成员”,不懂C++也没关系,“数据成员”这名字字面上就很形象。数据属性不需要声明,它像普通变量一样,在第一次赋值时产生。比如p是声明创建的Point的实例,则以下代码会打印数值8

p.times = 1
while p.times < 5:
    p.times = p.times * 2
print(p.times)
del p.times

虽然p.times并没有在类定义时声明(数据属性不需要声明),但在任何时候,我们可以给实例赋值一个新的数据属性(这里是p.times),并可以随时删除实例的数据属性(del p.times)。

方法,是“从属于”对象的函数。方法这个术语并不是类实例独有的,其它对象也可以有方法。比如,列表对象有append(), insert(), sort()等方法。

实例对象的有效方法名称依赖于其所属的类。 根据定义,一个类中所有是函数对象的属性都是定义了其实例的相应方法。 因此在我们的示例中,yrx.say_hi是有效的方法引用,因为YuanRenXue.say_hi是一个函数;而yrx.name不是方法,因为YuanRenXue.name不是一个函数。这里要注意,yrx.say_hiYuanRenXue.say_hi并不是一回事,它是一个方法对象,不是函数对象,通俗讲,前者是实例的方法,后者是类的函数。

方法对象

通常,调用方法的方法是:

yrx.say_hi()

在YuanRenXue 示例中,这将打印字符串Hello World。但是,调用一个方法也可以换另外一种形式,把它赋值给一个变量后再调用。例如:

yrx_say = yrx.say_hi
yrx_say()

当我们调用yrx.say_hi()时并没有带参数,但say_hi()函数定义时指定了一个参数。实际上,方法的特殊之处就是实例对象会作为函数的第一个参数(self)被传入。调用yrx.say_hi()其实就相当于YuanRenXue.say_hi(yrx)

类变量和实例变量

一般来说,类变量用于类的所有实例共享的属性和方法,而实例变量用于每个实例的唯一数据:

In [33]: class Tiger: 
    ...:     kind = "feline" 
    ...:     def __init__(self, name)                                         
  File "", line 3
    def __init__(self, name)
                            ^
SyntaxError: invalid syntax

In [34]: class Tiger: 
    ...:     kind = "feline" 
    ...:     def __init__(self, name): 
    ...:         self.name = name 
    ...:

In [35]: a = Tiger("Kiro")

In [36]: b = Tiger("Zim")

In [37]: a.kind
Out[37]: "feline"

In [38]: b.kind
Out[38]: "feline"

In [39]: a.name
Out[39]: "Kiro"

In [40]: b.name
Out[40]: "Zim"

如果类变量是列表或字典这种可变对象会导致令人惊讶的结果,我们要明白这种结果,从而可以在需要的时候利用这个特性,也可以在不需要的时候避免这个特性。是取是舍,全在于我们在编程时要定义的类的作用。下面,我们看看这个“令人惊讶”的结果是什么:

In [42]: class Tiger: 
    ...:     places = [] 
    ...:     def __init__(self, name): 
    ...:         self.name = name 
    ...:     def go_place(self, place): 
    ...:         self.places.append(place) 
    ...:

In [43]: a = Tiger("Kiro")

In [44]: b = Tiger("Zim")

In [45]: a.go_place("北京")

In [46]: b.go_place("上海")

In [47]: a.places
Out[47]: ["北京", "上海"]

这里,把places定义为类变量,它就记录了所有老虎(Kiro和Zim,以及后面实例化出来个各个老虎)去过的地方,所以,尽管a只去过北京,但是当我们通过a查看places,也看到了上海。这就是可变对象作为类变量时的特性。如果我们就是想记录所有老虎实例对象去过的地方,这样的用法就是恰到好处。

如果,我们只想记录每一只老虎去过的地方,那么就应该把places定义为实例变量,也就是在__init__()中进行初始化赋值。

需要注意的地方

(1)数据属性会覆盖掉具有相同名称的方法属性,因此写程序时为了避免名称冲突,可以使用某些规范来减少冲突,比如:

方法名称使用大写字母;

属性名称加上特殊的前缀(或加一个下划线);

用动词来命名方法,用名词来命名数据属性。

这些规范称为“代码规范”,一个好的代码规范让代码可读性更高,也更利用团队合作。有名的Python代码规范有“PEP 8”,也有Google的Python风格规范。

(2)数据属性可以被方法以及对象之外的任何用户(使用该类的人)所引用。也就是说,Python不行C++类那样有私有成员,Python没有任何对象能强制隐藏数据。

(3)类对象的用户应该谨慎使用数据属性,直接操作数据属性可能破坏其中的固定变量。但我们可以想一个实例对象添加我们自己的数据属性,但要避免与原有的名称冲突。

(4)方法的第一个参数常常被命名为self,代表实例对象。但这只是大家普通认同的一个约定。你可以用任何单词来替代它,但是你的代码让气体程序员读起来就很费劲,也会对VS Code这样的编辑器造成困惑。

总结

类是组合数据和功能的方法,其中:

数据,是类的属性,从属于类的各种数据对象;

功能,是类的方法,定义在类内部的各种函数。

定义好一个类我们就创建了一个类对象,类对象支持两种操作:属性引用和实例化。

类对象实例化后就得到了实例对象,它有两种属性名称:数据属性和方法。

类变量是所有类的实例共享的属性和方法,实例变量是每个实例独有的数据。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/43844.html

相关文章

  • python基础教程的继承

    摘要:类的继承,说明了不同类直接的关系,派生类复用了基类的代码同时也继承了基类的属性和方法。派生类的实例化会创建该类的一个新实例。派生类既可以单独继承一个基类,也可以多重继承多个基类。 面向对象语言的一个特性就是类的继承。继承的关系跟人类繁衍的关系相似,被继承的类称为基类(也叫做父类),继承而得的类叫派生类(也叫子类),这种关系就像人类的父子关系。 showImg(https://segme...

    XFLY 评论0 收藏0
  • python基础教程:错误处理和异常处理(二)

    摘要:语句就是帮助我们抛出知道异常的,比如的使用很简单,它的语法如下如果它后面不带表达式参数,它会重新引发当前作用域内最后一个激活的异常。,用于异常的串联。自定义的异常类的名称通常以错误结尾,类似与内置标准异常的命名。 前面我们讲到的Python编程过程中,在可能出现异常的地方使用尝试语句,来正确的处理一些异常,可以保证程序不中断继续运行。 showImg(https://segmentfa...

    wujl596 评论0 收藏0
  • Python爬虫学习路线

    摘要:以下这些项目,你拿来学习学习练练手。当你每个步骤都能做到很优秀的时候,你应该考虑如何组合这四个步骤,使你的爬虫达到效率最高,也就是所谓的爬虫策略问题,爬虫策略学习不是一朝一夕的事情,建议多看看一些比较优秀的爬虫的设计方案,比如说。 (一)如何学习Python 学习Python大致可以分为以下几个阶段: 1.刚上手的时候肯定是先过一遍Python最基本的知识,比如说:变量、数据结构、语法...

    liaoyg8023 评论0 收藏0
  • SegmentFault 技术周刊 Vol.30 - 学习 Python 来做一些神奇好玩的事情吧

    摘要:学习笔记七数学形态学关注的是图像中的形状,它提供了一些方法用于检测形状和改变形状。学习笔记十一尺度不变特征变换,简称是图像局部特征提取的现代方法基于区域图像块的分析。本文的目的是简明扼要地说明的编码机制,并给出一些建议。 showImg(https://segmentfault.com/img/bVRJbz?w=900&h=385); 前言 开始之前,我们先来看这样一个提问: pyth...

    lifesimple 评论0 收藏0

发表评论

0条评论

fish

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<