博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
零基础学习 Python 之细说类属性 & 实例
阅读量:5925 次
发布时间:2019-06-19

本文共 5506 字,大约阅读时间需要 18 分钟。

写在之前

如果你看过昨天的文章相信你对「类」有了一些基本的认识,为了能给之后的编程打个稍微牢固的基础,我们要深入到一些细节部分中去。今天我们来看类的属性。

类属性

首先我们在交互模式下先创建一个简单的类:

>>> class A():...    x = 1...复制代码

上面的 A() 类中的代码没有任何方法,只有 x = 1,当然如果你乐意的话,你可以写任何东西。先不管为什么,我们继续在交互模式下敲下面的代码:

>>> A.x1复制代码

A 是刚刚建立的类的名字,x 是类中的一个变量,它引用的对象是整数 1。通过 A.x 的方式就能得到整数 1,。像这样的,类中的 x 被称为类的属性,而 1 是这个属性的值,A.x 是调用类属性的方式。

我们在这里谈到了「属性」,请不要忽视这个词,在很多的领域都有它的身影。

下面我们回到之前 A 类的那个例子上。如果要调用类的某个属性,其方法是用英文的句号,就如我们例子中的 A.x。类的属性仅仅与其所定义的类绑定,并且这种属性本质上就是类里的变量。它的值不依赖任何的实例,只是由类中所写的变量赋值语句确定。所以类的属性还有另外一个名字 -- 「静态变量」。

我在前面的文章中说过很多次,在 Python 中 「万物皆对象」,类当然也不例外,它也是对象,凡是对象都具有属性和方法,而属性是可以增加删除和修改的。既然如此,那么对于之前的类 A,都可以对其目前所拥有的属性进行修改,也可以增加新的属性。

>>> A.y = 2>>> A.y2复制代码

上述代码给类 A 增加了一个新的属性 y,并赋值为 2。

>>> del A.x>>> A.xTraceback (most recent call last): File "
", line 1, in
AttributeError: class A has no attribute 'x'复制代码

上述代码删除了一个已有的属性 x,A.x 属性被删除后,如果再调用,就会出现异常。A.y 依然存在,我们可以修改 y 这个类的属性的值:

>>> A.y = 10000>>> A.y10000复制代码

y 是我们在 A 类中自己定义的属性,其实在一个类建立的同时,Python 也让这些类具有了一些默认的属性,可以用我们熟悉的 dir() 来查看类的所有属性,当然也包括方法:

>>> dir(A)['__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__', 'y']复制代码

我们仔细观察上面的结果,可以发现一个特殊的属性 dict,之所以用“特殊” 这个词来修饰,是因为它也是以双下划线开头和结尾的,类似于昨天文章中我们所见的 init()。在类里面,凡事以双下划线开头和结尾命名的属性和方法,我们都称它们为“特殊**”。

>>> A.__dict__mappingproxy({
'__module__': '__main__', 'y': 10000, '__dict__':
, '__weakref__':
, '__doc__': None})复制代码

下面我再说几种类的特殊属性的含义:

  • A.name:以字符串的形式返回类的名字,需要注意的是这时候得到的仅仅是一个字符串,而不是一个类对象。

  • A.doc:显示类的文档。

  • A.base:类 A 的所有父类。如果是按照上面方式定义的类,应该显示 object,因为以上所有的类都继承了它。等到学习了“继承”,再来看这个属性,内容就丰富了。

  • A.dict:以字典形式显示类的所有属性。

  • A.module:类所在的模块。

这里稍微解释一下 A.module,我们对类 A 做如下操作:

>>> A.__module__'__main__'复制代码

说明这个类所描述的模块是 mian()

最后让我们来对类的属性进行一个总结:

1.类属性跟类绑定,可以自定义,删除,修改值,也可以随时增加类属性。

2.每个类都有一些特殊属性,通常情况下特殊属性是不需要修改的,虽然有的特殊属性可以修改,比如 A.doc

对于类,除了属性,还有方法。但是类中的方法,因为牵扯到实例,所以我们还是通过研究实例来理解类中的方法。

我在之前的文章中说过,类是对象的定义,实例才是真实的东西。比如「人」 是一个类,但是「人”」终究不是具体的某个会喘气的,只有「rocky」 才是具体的东西,但他是具有「人」这个类所定义的属性和方法。「rocky」 就是「人」 这个类的实例。

创建实例

创建实例并不是很难的事情,只需要调用类就可以实现:

>>> class man():...     sex = '男'...>>> rocky = man()>>> rocky<__main__.man instance at 0x00000000004F3688>复制代码

如果不是用很严格的说法的话,上面的这个例子就是创建了一个实例 rocky。这里有一点需要我们注意的是,调用类的方法和调用类的函数类似,如果仅仅是写 man() 的话,则是创建了一个实例:

>>> man()<__main__.man instance at 0x0000000002577D88>复制代码

而 rocky = man() 本质上是将变量 rocky 与实例对象 man() 建立引用关系,这种关系就如同我们在刚开始的时候学的赋值语句 x = 1 是同样的效果。

那么对于一个实例来说这个建立的过程是怎么进行的呢?我们继续来看:

class Person:  """  具有通常类的结构的 Person 类  """  def __init__(self,name):      self.name = name  def get_name(self):      return self.name  def get_sex(self,sex):      per_sex = {}      per_sex[self.name] = sex      return per_sex复制代码

实例我们用 boy = Person('rocky') ,当然了,在这里你可以创建很多个实例,还记得那句话么:类是实例的工厂。

当我们创建完实例,接下来就是调用类,当类被调用以后,先是创建一个实例对象,然后检查是否有 init(),如果有的话就调用这个方法,并且将实例对象作为第一个参数 self 传进去,如果没有的话,就只是返回实例对象。

我之前也说过,init() 作为一个方法是比较特殊的,在它里面,一般是规定一些属性或者做一些初始化,让类具有一些基本的属性,但是它没有 return 语句,这是 init() 区别于一般方法的地方:

>>> class fun:...    def __init__(self):...            print('this is init()')...            return 1...>>> f = fun()this is init()Traceback (most recent call last): File "
", line 1, in
TypeError: __init__() should return None复制代码

上面的运行结果出现了异常,并且明确说明了「init() should return None」,所以不能有 return,如果非要带上的话,只能是 return None,索性就不要写了。

由此可知对于 init() ,除了第一个参数必须是 self,还要求不能有 return 语句,其他方面和普通函数就没有什么区别了。比如参数和里面的属性,你就可以像下面这样来做:

>>> class Person:...     def __init__(self,name,sex = '男',age = 10):...             self.name = name...             self.sex = sex...             self.age = age...复制代码

实例我们创建好了以后,我们接下来就要研究实例的内容,首先来看的是实例属性。

实例属性

和类属性相似,实例所具有的属性叫做 “实例属性”:

>>> class A:...     x = 1...>>> f = A()复制代码

类已经有了一个属性 A.x = 1,那么由类所创建的实例也应当具有这个属性:

>>> f.x1复制代码

除了 f.x 这个属性以外,实例也具有其它的属性和方法,我们依然用 dir 方法来看:

>>> dir(f)['__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__', 'x']复制代码

实例属性和类属性最主要的不同是在于,实例属性可以随意的更改:

>>> f.x += 10>>> f.x11复制代码

上面就是把实例属性修改了,但是类属性并没有因为实例属性的修改而发生变化,正如我们在前几天的文章中所说的那样,类属性是与类捆绑的,不受实例的影响。

>>> A.x1复制代码

上述的结果正好印证了这一点 -- 类属性不因实例属性改变而改变。既然如此,那么 f.x += 10 又改变了什么呢?

其实就是实例 f 又重新建立了一个新的属性,但是这个新的属性和原先旧的属性是一个名字,都是 f.x,所以相当于原先旧的属性被 “掩盖”了,只能访问到新的属性,所以值是 11。

>>> f.x11>>> del f.x>>> f.x1复制代码

由上面的例子可以看出,既然新的 f.x “掩盖”了旧的 f.x,只要把新的 f.x 删除,旧的 f.x 就可以显现出来。

实例的改变不会影响到类,但是类属性可以影响到实例属性,因为实例就是通过调用类来建立的:

>>> A.x += 10>>> A.x11>>> f.x11复制代码

如果是同一个属性 x,那么实例属性跟着类属性的改变而改变,当然,这个是针对于像字符串这种不可变对象而言的,对于类中如果引用的是可变对象的数据,则情形会有所不同,因为可变对象的数据是可以原地进行修改的:

>>> class B:...     y = [1,2,3,4]...>>> B.y #类属性[1, 2, 3, 4]>>> f = B()>>> f.y #实例属性[1, 2, 3, 4]>>> B.y.append('5')>>> B.y[1, 2, 3, 4, '5']>>> f.y[1, 2, 3, 4, '5']>>> f.y.append('66')>>> B.y[1, 2, 3, 4, '5', '66']>>> f.y[1, 2, 3, 4, '5', '66']复制代码

通过上面的代码我们可以看出,当类中的变量引用的是可变对象的时候,类属性和实例属性都能够直接修改这个对象,从而增加另一方的值。

还有一点我们已经知道了增加一个类属性,相应的实例属性也会增加,但是反过来就不成立了:

>>> B.x = 'aa'>>> f.x'aa'>>> f.z = 'abcd'>>> f.z'abcd'>>> B.zTraceback (most recent call last): File "
", line 1, in
AttributeError: class B has no attribute 'z'复制代码

可以看出类并没有接纳实例实例增加的属性。

写在之后

更多内容,欢迎关注公众号「Python空间」,期待和你的交流。

转载地址:http://vgivx.baihongyu.com/

你可能感兴趣的文章
程序员应知道的12件事
查看>>
Video: Connecting Android applications with DataSnap Server – Delphi Conference Brazil 2010
查看>>
电子书下载:iPhone and iPad Apps for Absolute Beginners iOS 5 Edition
查看>>
innosetup安装之前关闭进程
查看>>
关于RDA远程访问数据库的一个例子(亲手完成,不容易啊)
查看>>
原始方法(失去光标验证数据库)
查看>>
Cookies揭秘 [Asp.Net, Javascript]
查看>>
检测远程URL是否存在
查看>>
TCP/IP笔记 三.运输层(2)——TCP 流量控制与拥塞控制
查看>>
Select的动态取值(Text,value),添加,删除。兼容IE,FireFox
查看>>
百度技术沙龙(第2期)- 2. 互联网应用服务扩展的一点经验(转载)
查看>>
Java IO类图
查看>>
Restful Web Service初识
查看>>
MVC扩展控制器, 把部分视图转换成字符串(带验证信息), 并以json传递给前端视图...
查看>>
AngularJS中自定义过滤器
查看>>
python参考手册--第10、11章执行环境、调试
查看>>
.Net 加密原理,HVM核心的实现原理(八)
查看>>
MHz 和 Mbps的区别
查看>>
qualcomm platform camera porting
查看>>
OpenSSL开发学习总结
查看>>