Skip to content

Commit b050101

Browse files
committed
clsss 3
1 parent 2d7a895 commit b050101

File tree

6 files changed

+353
-9
lines changed

6 files changed

+353
-9
lines changed

208.md

+337
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,337 @@
1+
>世人凭自己的智慧,既不认识神,神就乐意用人所当作愚拙的道理拯救那些信的人,这就是神的智慧了。犹太人是要神迹,希腊人是求智慧;我们却是传钉十字架的基督。(1 CORINTHIANS 2:21-22)
2+
3+
#类(3)
4+
5+
在上一节中,对类有了基本的或者说是模糊的认识,为了能够对类有更深刻的认识,本节要深入到一些细节。
6+
7+
##类属性和实例属性
8+
9+
正如上节的案例中,一个类实例化后,实例是一个对象,有属性。同样,类也是一个对象,它也有属性。
10+
11+
>>> class A(object):
12+
... x = 7
13+
...
14+
15+
在交互模式下,定义一个很简单的类(注意观察,有`(object)`,是新式类),类中有一个变量`x = 7`,当然,如果愿意还可以写别的。因为一下操作中,只用到这个,我就不写别的了。
16+
17+
>>> A.x
18+
7
19+
20+
在类A中,变量x所引用的数据,能够直接通过类来调用。或者说x是类A的属性,这种属性有一个名称,曰“类属性”。类属性仅限于此——类中的变量。它也有其他的名字,如静态数据。
21+
22+
>>> foo = A()
23+
>>> foo.x
24+
7
25+
26+
实例化,通过实例也可以得到这个属性,这个属性叫做“实例属性”。对于同一属性,可以用类来访问(类属性),在一般情况下,也可以通过实例来访问同样的属性。但是:
27+
28+
>>> foo.x += 1
29+
>>> foo.x
30+
8
31+
>>> A.x
32+
7
33+
34+
实例属性更新了,类属性没有改变。这至少说明,类属性不会被实例属性左右,也可以进一步说“类属性与实例属性无关”。那么,`foo.x += 1`的本质是什么呢?其本质是该实例foo又建立了一个新的属性,但是这个属性(新的foo.x)居然与原来的属性(旧的foo.x)重名,所以,原来的foo.x就被“遮盖了”,只能访问到新的foo.x,它的值是8.
35+
36+
>>> foo.x
37+
8
38+
>>> del foo.x
39+
>>> foo.x
40+
7
41+
42+
既然新的foo.x“遮盖”了旧的foo.x,如果删除它,旧的不久显现出来了?的确是。删除之后,foo.x就还是原来的值。此外,还可以通过建立一个不与它重名的实例属性:
43+
44+
>>> foo.y = foo.x + 1
45+
>>> foo.y
46+
8
47+
>>> foo.x
48+
7
49+
50+
foo.y就是新建的一个实例属性,它没有影响原来的实例属性foo.x。
51+
52+
但是,类属性能够影响实例属性,这点应该好理解,因为实例就是通过实例化调用类的。
53+
54+
>>> A.x += 1
55+
>>> A.x
56+
8
57+
>>> foo.x
58+
8
59+
60+
这时候实例属性跟着类属性而改变。
61+
62+
以上所言,是指当类中变量引用的是不可变数据。如果类中变量引用可变数据,情形会有所不同。因为可变数据能够进行原地修改。
63+
64+
>>> class B(object):
65+
... y = [1,2,3]
66+
...
67+
68+
这次定义的类中,变量引用的是一个可变对象。
69+
70+
>>> B.y #类属性
71+
[1, 2, 3]
72+
>>> bar = B()
73+
>>> bar.y #实例属性
74+
[1, 2, 3]
75+
76+
>>> bar.y.append(4)
77+
>>> bar.y
78+
[1, 2, 3, 4]
79+
>>> B.y
80+
[1, 2, 3, 4]
81+
82+
>>> B.y.append("aa")
83+
>>> B.y
84+
[1, 2, 3, 4, 'aa']
85+
>>> bar.y
86+
[1, 2, 3, 4, 'aa']
87+
88+
从上面的比较操作中可以看出,当类中变量引用的是可变对象是,类属性和实例属性都能直接修改这个对象,从而影响另一方的值。
89+
90+
对于类属性和实例属性,除了上述不同之外,在下面的操作中,也会有差异。
91+
92+
>>> foo = A()
93+
>>> dir(foo)
94+
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'x']
95+
96+
实例化类A,可以查看其所具有的属性(看最后一项,x),当然,执行`dir(A)`也是一样的。
97+
98+
>>> A.y = "hello"
99+
>>> foo.y
100+
'hello'
101+
102+
增加一个类属性,同时在实例属性中也增加了一样的名称和数据的属性。如果增加通过实例增加属性呢?看下面:
103+
104+
>>> foo.z = "python"
105+
>>> foo.z
106+
'python'
107+
>>> A.z
108+
Traceback (most recent call last):
109+
File "<stdin>", line 1, in <module>
110+
AttributeError: type object 'A' has no attribute 'z'
111+
112+
类并没有收纳这个属性。这进一步说明,类属性不受实例属性左右。另外,在类确定或者实例化之后,也可以增加和修改属性,其方法就是通过类或者实例的点号操作来实现,即`object.attribute`,可以实现对属性的修改和增加。
113+
114+
##数据流转
115+
116+
在类的应用中,最广泛的是将类实例化,通过实例来执行各种方法。所以,对此过程中的数据流转一定要弄明白。
117+
118+
回顾上节已经建立的那个类,做适当修改,并请出"canglaoshi"。但是,我将注释删除,读者是否能够写上必要的注释呢?如果你把注释写上,就已经理解了类的基本结构。
119+
120+
#!/usr/bin/env python
121+
# coding=utf-8
122+
123+
__metaclass__ = type
124+
125+
class Person:
126+
def __init__(self, name):
127+
self.name = name
128+
129+
def getName(self):
130+
return self.name
131+
132+
def breast(self, n):
133+
self.breast = n
134+
135+
def color(self, color):
136+
print "%s is %s" % (self.name, color)
137+
138+
def how(self):
139+
print "%s breast is %s" % (self.name, self.breast)
140+
141+
girl = Person('canglaoshi')
142+
girl.breast(90)
143+
144+
girl.color("white")
145+
girl.how()
146+
147+
运行后结果:
148+
149+
$ python 20701.py
150+
canglaoshi is white
151+
canglaoshi breast is 90
152+
153+
一图胜千言,有图有真相。通过图示,我们看一看数据的流转过程。
154+
155+
![](./2images/20801.png)
156+
157+
创建实例`girl = Person('canglaoshi')`,注意观察图上的箭头方向。girl这个实例和Person类中的self对应,这正是应了上节所概括的“实例变量与self对应,实例变量主外,self主内”的概括。"canglaoshi"是一个具体的数据,通过初始化函数中的name参数,传给self.name,前面已经讲过,self也是一个实例,可以为它设置属性,`self.name`就是一个属性,经过初始化函数,这个属性的值由参数name传入,现在就是"canglaoshi"。
158+
159+
在类Person的其它方法中,都是以self为第一个或者唯一一个参数。注意,在python中,这个参数要显明写上,在类内部是不能省略的。这就表示所有方法都承接self实例对象,它的属性也被带到每个方法之中。例如在方法里面使用`self.name`即是调用前面已经确定的实例属性数据。当然,在方法中,还可以继续为实例self增加属性,比如`self.breast`。这样,通过self实例,就实现了数据在类内部的流转。
160+
161+
如果要把数据从类里面传到外面,可以通过`return`语句实现。如上例子中所示的`getName`方法。
162+
163+
因为实例名称(girl)和self是对应关系,实际上,在类里面也可以用girl代替self。例如,做如下修改:
164+
165+
#!/usr/bin/env python
166+
# coding=utf-8
167+
168+
__metaclass__ = type
169+
170+
class Person:
171+
def __init__(self, name):
172+
self.name = name
173+
174+
def getName(self):
175+
#return self.name
176+
return girl.name #修改成这个样子,但是在编程实践中不要这么做。
177+
178+
girl = Person('canglaoshi')
179+
name = girl.getName()
180+
print name
181+
182+
运行之后,打印:
183+
184+
canglaoshi
185+
186+
这个例子说明,在实例化之后,实例变量girl和函数里面的那个self实例是完全对应的。但是,提醒读者,千万不要用上面的修改了的那个方式。因为那样写使类没有独立性,这是大忌。
187+
188+
##命名空间
189+
190+
命名空间,英文名字:namespaces。在研究类或者面向对象编程中,它常常被提到。
191+
192+
在研习命名空间以前,请打开在python的交互模式下,输入:import this
193+
194+
>>> import this
195+
The Zen of Python, by Tim Peters
196+
197+
Beautiful is better than ugly.
198+
Explicit is better than implicit.
199+
Simple is better than complex.
200+
Complex is better than complicated.
201+
Flat is better than nested.
202+
Sparse is better than dense.
203+
Readability counts.
204+
Special cases aren't special enough to break the rules.
205+
Although practicality beats purity.
206+
Errors should never pass silently.
207+
Unless explicitly silenced.
208+
In the face of ambiguity, refuse the temptation to guess.
209+
There should be one-- and preferably only one --obvious way to do it.
210+
Although that way may not be obvious at first unless you're Dutch.
211+
Now is better than never.
212+
Although never is often better than *right* now.
213+
If the implementation is hard to explain, it's a bad idea.
214+
If the implementation is easy to explain, it may be a good idea.
215+
Namespaces are one honking great idea -- let's do more of those!
216+
217+
这里列位看到的就是所谓《python之禅》,请看最后一句: Namespaces are one honking great idea -- let's do more of those!
218+
219+
这是为了向看官说明Namespaces、命名空间值重要性。
220+
221+
###什么是命名空间
222+
223+
从“一切皆为对象”开始说起吧。对象,很多时候我们直接使用它并不方便,因此要给它取一个名字。打个比方,有这样一个物种,它是哺乳纲灵长目人科人属智人种,这就是所谓的对象,但是,在平时提及这个对象的时候,总是要说“哺乳纲灵长目人科人属智人种”,是不是太麻烦了?于是聪明的这个物种就为这个世界上的各种对象命名,例如将“哺乳纲灵长目人科人属智人种”这个对象命名为“人”。
224+
225+
在编程中也是如此,前面在讲述变量相关知识的时候已经说明了变量和引用对象的关系。
226+
227+
>>> a = 7
228+
>>> id(7)
229+
137589400
230+
>>> id(a)
231+
137589400
232+
>>> id(7) == id(a)
233+
True
234+
235+
看这个例子。7就是一个计算机内存中存在的对象,用id()这个内置函数可以查看7在内存(在RAM)中的地址。a 就是为这个对象预备的名字,如前面所讲的,它与内存中的一个编号为137589400的对象关联,或者说引用了这个对象,这个对象就是7.
236+
237+
如果做了下面的操作:
238+
239+
>>> a = a + 1
240+
>>> id(a)
241+
137589388
242+
>>> a
243+
8
244+
>>> id(8)
245+
137589388
246+
247+
其实,上面操作中的`a + 1`完成的是a引用的对象`7+1`,只不过是顺着对象7的命名a导入了对象7罢了,这样就在内存中建立了一个新的对象8,同样通过id()函数查看到内存中的地址,通过地址可以看到,这时候的a又自动引用对象8了.
248+
249+
>>> id(7) #对象7在内存中的地址没变
250+
137589400
251+
>>> b = 7 #b引用此对象
252+
>>> id(b)
253+
137589400
254+
255+
上面a转换引用对象的过程,是自动完成的。而当b=7的时候,并不是在内存中从新建立一个对象7,而是b引用了已有的对象。这就是python的所谓动态语言的特点。
256+
257+
![](./2images/20802.png)
258+
259+
当然,可以给任何对象取名字,或者说为任何对象都可以建立一个所引用的变量。比如函数、类都可以,此处不赘述,前面已经多次用到了。
260+
261+
现在已经又一次明确了,每个名称(命名)——英文中的NAME有动词和名字两种,所以,由于中文的特点,似乎怎么说都可以,只要明白所指,因为中文是强调语境的语言——都与某个对象有对应关系。那么所谓的**命名空间,就是这些命名(名称)的集合,它们分别与相应的对象有对应关系。**
262+
263+
用一句比较学术化的语言说:
264+
265+
**命名空间是从所定义的命名到对象的映射集合。**
266+
267+
不同的命名空间,可以同时存在,当彼此相互独立互不干扰。
268+
269+
命名空间因为对象的不同,也有所区别,可以分为如下几种:
270+
271+
- 内置命名空间(Built-in Namespaces):Python运行起来,它们就存在了。内置函数的命名空间都属于内置命名空间,所以,我们可以在任何程序中直接运行它们,比如前面的id(),不需要做什么操作,拿过来就直接使用了。
272+
- 全局命名空间(Module:Global Namespaces):每个模块创建它自己所拥有的全局命名空间,不同模块的全局命名空间彼此独立,不同模块中相同名称的命名空间,也会因为模块的不同而不相互干扰。
273+
- 本地命名空间(Function&Class: Local Namespaces):模块中有函数或者类,每个函数或者类所定义的命名空间就是本地命名空间。如果函数返回了结果或者抛出异常,则本地命名空间也结束了。
274+
275+
从网上盗取了一张图,展示一下上述三种命名空间的关系
276+
277+
![](./2images/20803.png)
278+
279+
那么程序在查询上述三种命名空间的时候,就按照从里到外的顺序,即:Local Namespaces --> Global Namesspaces --> Built-in Namesspaces
280+
281+
还要补充说一下,既然命名空间中存在着命名和对象的映射,不知道看官看到这句话能想到什么?启发一下,回忆以往学过的那种类型数据也存在映射关系呢?字典,就是那个dictionary,是“键值”对应的,例如:{"name":"qiwsir","lang":"python"}
282+
283+
>>> def foo(num,str):
284+
... name = "qiwsir"
285+
... print locals()
286+
...
287+
>>> foo(221,"qiwsir.github.io")
288+
{'num': 221, 'name': 'qiwsir', 'str': 'qiwsir.github.io'}
289+
>>>
290+
291+
这是一个访问本地命名空间的方法,用print locals() 完成,从这个结果中不难看出,所谓的命名空间中的数据存储结构和dictionary是一样的。
292+
293+
根据习惯,看官估计已经猜测到了,如果访问全局命名空间,可以使用 `print globals()`
294+
295+
###作用域
296+
297+
作用域是指 Python 程序可以直接访问到的命名空间。“直接访问”在这里意味着访问命名空间中的命名时无需加入附加的修饰符。(这句话是从网上抄来的)
298+
299+
程序也是按照搜索命名空间的顺序,搜索相应空间的能够访问到的作用域。
300+
301+
def outer_foo():
302+
b = 20
303+
def inner_foo():
304+
c = 30
305+
a = 10
306+
307+
假如我现在位于inner_foo()函数内,那么c对我来讲就在本地作用域,而b和a就不是。如果我在inner_foo()内再做:b=50,这其实是在本地命名空间内新创建了对象,和上一层中的b=20毫不相干。可以看下面的例子:
308+
309+
#!/usr/bin/env python
310+
#coding:utf-8
311+
312+
def outer_foo():
313+
a = 10
314+
def inner_foo():
315+
a = 20
316+
print "inner_foo,a=",a #a=20
317+
318+
inner_foo()
319+
print "outer_foo,a=",a #a=10
320+
321+
a = 30
322+
outer_foo()
323+
print "a=",a #a=30
324+
325+
#运行结果
326+
327+
inner_foo,a= 20
328+
outer_foo,a= 10
329+
a= 30
330+
331+
如果要将某个变量在任何地方都使用,且能够关联,那么在函数内就使用global 声明,其实就是曾经讲过的全局变量。
332+
333+
------
334+
335+
[总目录](./index.md)&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;[上节:类(2)](./207.md)&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;[下节:类(4)](./209.md)
336+
337+
如果你认为有必要打赏我,请通过支付宝:**[email protected]**,不胜感激。

2code/20701.py

+14-8
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,25 @@
66
class Person:
77
def __init__(self, name):
88
self.name = name
9-
print self
10-
print type(self)
119

1210
def getName(self):
13-
return self.name
14-
11+
#return self.name
12+
return girl.name
13+
14+
def breast(self, n):
15+
self.breast = n
16+
1517
def color(self, color):
1618
print "%s is %s" % (self.name, color)
1719

20+
def how(self):
21+
print "%s breast is %s" % (self.name, self.breast)
22+
1823
girl = Person('canglaoshi')
1924
name = girl.getName()
20-
print "the person's name is: ", name
21-
girl.color("white")
25+
print name
2226

23-
print "------"
24-
print girl.name
27+
girl.breast(90)
28+
29+
girl.color("white")
30+
girl.how()

2images/20801.png

43.6 KB
Loading

2images/20802.png

14.2 KB
Loading

2images/20803.png

58.1 KB
Loading

index.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
1. [集成开发环境](./101.md)==>集成开发环境;python的IDE
1616
2. [数和四则运算](./102.md)==>整数和浮点数;变量;整数溢出问题;
17-
3. [除法](./103.md)==>整数、浮点数相除;from __future__ import division;余数;四舍五入;
17+
3. [除法](./103.md)==>整数、浮点数相除;`from __future__ import division`;余数;四舍五入;
1818
4. [常用数学函数和运算优先级](./104.md)==>math模块,求绝对值,运算优先级
1919
5. [写一个简单程序](./105.md)==>程序和语句,注释
2020
6. [字符串(1)](./106.md)==>字符串定义,转义符,字符串拼接,str()与repr()区别
@@ -52,6 +52,7 @@
5252
5. [函数练习](./205.md)==>解一元二次方程,统计考试成绩,找素数
5353
6. [类(1)](./206.md)==>类的初步认识和基本概念理解:问题空间、对象、面向对象、类和实例化类
5454
7. [类(2)](./207.md)==>新式类和旧式类,类的命名,构造函数,实例化及方法和属性,self的作用
55+
8. [类(3)](./208.md)==>类属性和实例属性,类内外数据流转,命名空间、作用域
5556

5657
##第三部分:模块
5758

0 commit comments

Comments
 (0)