最近闲余在看Python的书,吐槽一下

2012-05-09 11:20:43 +08:00
 levon
关于类的,代码:

class MemberCounter:
>>>>members = 0
>>>>def init(self):
>>>>>>>>MemberCounter.members += 1


m1 = MemberCounter()
m1.init()

print MemberCounter.members

m2 = MemberCounter()

print MemberCounter.members

print m1.members
print m2.members

输出
1
2
2
2

===============================
1. 类明明就是抽象的东西,怎么可以MemberCounter.members这样去访问呢,困惑
2. m2 = MemberCounter()不是新建个instance吗,怎么跟m1也能扯上关系,而且m1.members和m2.members都一样,唉,谁知道类会不会被别人调用,被别人调用会不会导致混乱呢。

实战过的朋友帮忙释疑吧,我都不忍再往下看书了。
4842 次点击
所在节点    Python
18 条回复
CoX
2012-05-09 11:36:58 +08:00
Python 2.6.6 (r266:84297, Aug 24 2010, 18:46:32) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> class MemberCounter:
... members = 0
... def init(self):
... MemberCounter.members += 1
...
>>>
>>> m1 = MemberCounter()
>>> m1.init()
>>>
>>> print MemberCounter.members
1
>>>
>>> m2 = MemberCounter()
>>>
>>> print MemberCounter.members
1
>>>
>>> print m1.members
1
>>> print m2.members
1

不知道lz的输出是怎么回事
vayn
2012-05-09 11:38:19 +08:00
members 是类属性,而非实例属性
Cofyc
2012-05-09 11:38:36 +08:00
学 Python 时,记住一句话:"Everything Is an Object"

一切都是对象,你就明白了。

这里,类 MemberCounter 也是对象。

包括类的类型(type)也是对象,type 的 type 是其自身。
levon
2012-05-09 11:48:05 +08:00
@CoX 谢谢回复,我是写入一个文档,然后执行文档的
levon
2012-05-09 11:50:15 +08:00
@vayn 有类属性和实例属性之分吗,你意思是不是我实例化一个类得到一个新的instance后可以为这个实例添加新的属性?
Cofyc
2012-05-09 11:51:19 +08:00
lz 贴的代码有问题,如果按楼主输出,应该少了 m2.init() :

m2 = MemberCounter()
m2.init()

---
这段代码里就是在两次 init() 调用中,自增了类对象 MemberCounter 的属性 members。

m1, m2 是 MemberCounter 对象的实例,如果要自增 m1/m2 的 members 属性,需要使用 self.members 访问。
allenm
2012-05-09 11:51:46 +08:00
@CoX LZ 的代码少了一行 m2.init()

我猜LZ是看的 《Python 基础教程》这本书吧,我看到这个问题,翻了下手边的这本书的相应部分,就看到这些代码了。

LZ尝试把 init 方法的定义改成 self.members += 1 试试看效果。

这个其实也不难理解吧,即使在 JAVA 这种静态语言中,也可以定义一个 static 的属性,可以直接通过 className.proerty 这样访问的。

像 LS 说的,Python 中,要记住“ Everything is an Object "

好吧,我是 python 菜鸟, java 更菜,如果说错了,欢迎指正。
levon
2012-05-09 11:52:09 +08:00
@Cofyc 哦,是的,我忘记写这句了,实际文档中是有的
eerie
2012-05-09 11:54:16 +08:00
cpp也有static member,不知道楼主啥意思
agui
2012-05-09 11:56:21 +08:00
看的是<python基础教程>吧? 我最近也在看,有问题大家可以一起交流下:)
levon
2012-05-09 12:05:03 +08:00
@agui 是的,是这本。

@eerie你说的static member,这种成员是不是每个实例都是一样的值?

我知道python就是这样的。书上也有说SmallTalk之父反对这种,说对象特性只允许同一个对象的方法访问。觉得Python这样做破坏了封装的原则。我看得有点模糊,一知半解
ipoh
2012-05-09 12:08:11 +08:00
1. 类明明就是抽象的东西,怎么可以MemberCounter.members这样去访问呢
类也是一个对象

2. m2 = MemberCounter()不是新建个instance吗,怎么跟m1也能扯上关系,而且m1.members和m2.members都一样,唉,谁知道类会不会被别人调用,被别人调用会不会导致混乱呢。
python成员变量请用self.members
eerie
2012-05-09 12:20:09 +08:00
@levon 哦,我看楼上几位的回复理解你的意思了
我还以为你知道members是个static variable呢

通过MemberCounter.members调用的是class自身的变量,不是instance的,于是就相当于static的
你没有给instance本身生成members这个变量,你访问的时候应该fallback到class的static变量了?(见我下面的例子)
另外,我仔细看了下你给的那个例子。。。我猜init应该写成__init__,而且并不需要你手动调用吧...

>>> class a():
... n = 0
...
>>> b = a()
>>> a.n = 100
>>> a.n
100
>>> b.n
100
bravluna
2012-05-09 12:20:12 +08:00
LZ 可能受了 Java 这类语言的影响,在 py 里类只是个逻辑概念,实质上是个命名空间,模块和函数都是,更实质上都是个字典(可以查看其__dict__属性),类和实例对象稍特殊一些,可以继承(__class__,__base__),所以:

1. 可以这样引用,在 py 里,一切对象都可以用 obj.attr 的形式引用其属性。

2. 赋值操作才会给给对象增加属性,按以上代码 m1 和 m2 字典里根本没有 members 属性,只是继承了 class 的 member 属性。
chainchan
2012-05-09 13:01:01 +08:00
1,MemberCounter.members中的members你直接声明在类的下面,那么你可以把他想成是一个static变量。
2,m1和m2是MemberCounter的实例,而且他们是不同的
>>> id(m1) != id(m2)
True
但是m1.members和m2.members是相同的
>>> id(m1.member) == id(m2.member)
True
因为他们同时引用的是MemberCounter.members

此外,在Python中,所有的类属性都是公开的,所以需要在设计时需要考虑清楚,以免用户通过不规范的操作来存取封装的数据属性。而且还要告诉你一点,Python提供了动态增加实例属性的特点,比如你可以这样玩。m1.newguy='chainchan' 很方便,但是后果自负。然后你可能会想在C++或者Java里面提供私有性质的访问,Python也提供了__slot__属性。
sampeng
2012-05-09 13:31:13 +08:00
我觉得应该这么理解python
面向对象,更深的说应该是面向接口。
任何你使用的对象,包括函数都是接口。只要参数传进来他就不管这个货到底是什么。只要有函数自己需要的接口,就会有正确的行为。

这是极灵活的。。。。虽然其他的语言也是这么一说,但是python直接作为强制行为了。为什么python类的属性是没有私有一说的。就是这个原因。只要定义了,那就是一个对外的接口
levon
2012-05-09 14:20:53 +08:00
@chainchan 谢谢,你的解释让我有用
hwywhywl
2012-05-09 14:28:14 +08:00
1. MemberCounter 是 type 一个 实例,members 是 MemberCounter 一个属性,生成MemberCounter类的时候,其实是实例化 一个 type。

2. m2是MemberCounter实例,m2 和 MemberCounter 都有一个类似 __dict__ 的属性,
m2.members 查找过程:
(1) 如果 m2.__dict__ 中有名为 members 的key,则返回 m2.__dict__['members'],如果没有则进入(2)。
(2) 如果 MemberCounter.__dict__ 中有名为 members 的key,则返回 MemberCounter.__dict__['members'],如果没有则进入(3)。
(3) 如果m2没有定义__getatt__(self, name),则抛出attributeerror异常,如果定义有__getatt__(self, name) 则看它如何处理。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/35205

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX