内容简介:如果你看过昨天的文章相信你对「类」有了一些基本的认识,为了能给之后的编程打个稍微牢固的基础,我们要深入到一些细节部分中去。今天我们来看类的属性。首先我们在交互模式下先创建一个简单的类:上面的 A() 类中的代码没有任何方法,只有 x = 1,当然如果你乐意的话,你可以写任何东西。先不管为什么,我们继续在交互模式下敲下面的代码:
如果你看过昨天的文章相信你对「类」有了一些基本的认识,为了能给之后的编程打个稍微牢固的基础,我们要深入到一些细节部分中去。今天我们来看类的属性。
类属性
首先我们在交互模式下先创建一个简单的类:
>>> class A(): ... x = 1 ... 复制代码
上面的 A() 类中的代码没有任何方法,只有 x = 1,当然如果你乐意的话,你可以写任何东西。先不管为什么,我们继续在交互模式下敲下面的代码:
>>> A.x 1 复制代码
A 是刚刚建立的类的名字,x 是类中的一个变量,它引用的对象是整数 1。通过 A.x 的方式就能得到整数 1,。像这样的,类中的 x 被称为类的属性,而 1 是这个属性的值,A.x 是调用类属性的方式。
我们在这里谈到了「属性」,请不要忽视这个词,在很多的领域都有它的身影。
下面我们回到之前 A 类的那个例子上。如果要调用类的某个属性,其方法是用英文的句号,就如我们例子中的 A.x。类的属性仅仅与其所定义的类绑定,并且这种属性本质上就是类里的变量。它的值不依赖任何的实例,只是由类中所写的变量赋值语句确定。所以类的属性还有另外一个名字 -- 「静态变量」。
我在前面的文章中说过很多次,在 Python 中 「万物皆对象」,类当然也不例外,它也是对象,凡是对象都具有属性和方法,而属性是可以增加删除和修改的。既然如此,那么对于之前的类 A,都可以对其目前所拥有的属性进行修改,也可以增加新的属性。
>>> A.y = 2 >>> A.y 2 复制代码
上述代码给类 A 增加了一个新的属性 y,并赋值为 2。
>>> del A.x >>> A.x Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: class A has no attribute 'x' 复制代码
上述代码删除了一个已有的属性 x,A.x 属性被删除后,如果再调用,就会出现异常。A.y 依然存在,我们可以修改 y 这个类的属性的值:
>>> A.y = 10000 >>> A.y 10000 复制代码
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__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__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 "<stdin>", line 1, in <module> 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.x 1 复制代码
除了 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.x 11 复制代码
上面就是把实例属性修改了,但是类属性并没有因为实例属性的修改而发生变化,正如我们在前几天的文章中所说的那样,类属性是与类捆绑的,不受实例的影响。
>>> A.x 1 复制代码
上述的结果正好印证了这一点 -- 类属性不因实例属性改变而改变。既然如此,那么 f.x += 10 又改变了什么呢?
其实就是实例 f 又重新建立了一个新的属性,但是这个新的属性和原先旧的属性是一个名字,都是 f.x,所以相当于原先旧的属性被 “掩盖”了,只能访问到新的属性,所以值是 11。
>>> f.x 11 >>> del f.x >>> f.x 1 复制代码
由上面的例子可以看出,既然新的 f.x “掩盖”了旧的 f.x,只要把新的 f.x 删除,旧的 f.x 就可以显现出来。
实例的改变不会影响到类,但是类属性可以影响到实例属性,因为实例就是通过调用类来建立的:
>>> A.x += 10 >>> A.x 11 >>> f.x 11 复制代码
如果是同一个属性 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.z Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: class B has no attribute 'z' 复制代码
可以看出类并没有接纳实例实例增加的属性。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Big Java Late Objects
Horstmann, Cay S. / 2012-2 / 896.00元
The introductory programming course is difficult. Many students fail to succeed or have trouble in the course because they don't understand the material and do not practice programming sufficiently. ......一起来看看 《Big Java Late Objects》 这本书的介绍吧!