python中关于类的属性的一个问题

qingfeng__ 在校大学生  2017-11-22 10:12:23


对于python中一个类的属性的一个问题。为什么当我的一个属性为普通常量时,这个数不随着我类的实例的增加而改变呢?而我的一个属性为列表时,我的列表值却随着实例的增加而产生了改变。(已测试,与属性的私有公有无关。)有大神遇到过这个问题吗?
...全文
172 8 点赞 打赏 收藏 举报
写回复
8 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
好想长大 2020-01-10
我和你遇到了同样的问题,但是解决了,类的h属性和实例的h属性是同一个,所以会这样。
  • 打赏
  • 举报
回复
混沌鳄鱼 2017-11-23
楼主这个问题涉及到了Python两个方面的概念 (1)类变量和实例变量的概念 (2)不可变对象和可变对象的概念。 建议楼主分别百度一下python 的这个两个概念,实际上只要你认真看书的话,基本上每本Python书都有讲解。 你在__init__() 中, self.__h = self.__h + 1 实际上,前后两个self.__h 是不同的, 前一个是要声明的实例变量(属性), 后一个则是引用了从类继承来的类变量(属性)。 一个对象的属性查找顺序遵循首先查找实例对象自己,然后是类,接着是类的父类。 通常每个对象都会有一个__dict__属性,是个字典,用来存放对象的属性和值 举栗子:

>>> class T():
	__h = 0
	def __init__(self):
		self.__h = self.__h + 1
		print(self.__h)
>>> T.__dict__
mappingproxy({'__module__': '__main__', '_T__h': 0, '__init__': <function T.__init__ at 0x0000000001D33E18>, '__dict__': <attribute '__dict__' of 'T' objects>, '__weakref__': <attribute '__weakref__' of 'T' objects>, '__doc__': None})
>>> id(T._T__h)
1499448976
>>> t1 = T()
1
>>> t1.__dict__
{'_T__h': 1}
>>> T._T__h
0
>>> T._T__h = 5
>>> t2 = T()
6
>>> t2._T__h
6
>>> 

那么要实现期望的那种每新建一个实例,实现变量值累加也是可以的。 例如楼上同学直接使用类名引用类变量来做所有实例共享累加。 另外一个方法,就是要利用可变对象传引用,可以原地修改值的特性。 再来一个例子

>>> class T:
	h = [0]
	def __init__(self):
		self.h[0] = self.h[0] + 1
		print(self.h[0])

		
>>> id(T.h)
48528136
>>> t1 = T()
1
>>> id(t1.h)
48528136
>>> t2 = T()
2
>>> id(t2.h)
48528136
>>> t3 = T()
3
>>> id(t3.h)
48528136
>>> 

  • 打赏
  • 举报
回复
extend 2017-11-23
楼上说的对,就是类变量和实例变量的问题。但是和不可变对象/可变对象没关系。 >>> class T(): __h=0 def __init__(self): self.__h=self.__h+1 print(self.__h) print(T.__h) >>> t1=T() 1 #实例变量 0 #类变量 ==========比较下面这个例子======== >>> class T(): __h=0 def __init__(self): T.__h=T.__h+1 print(self.__h) print(T.__h) >>> t3=T() 1 #实例变量 1 #类变量 要实现类实例的计数,把计数器定义在类变量中: >>> class T(): __h=0 def __init__(self): T.__h=T.__h+1 #print(self.__h) print(T.__h) >>> save=[] >>> for i in range(0,10): save.append(T()) 1 2 3 4 5 6 7 8 9 10
  • 打赏
  • 举报
回复
qingfeng__ 2017-11-23
引用 1 楼 QuantumEnergy 的回复:
class Bar(object):
    i = 0
    j = []
    print id(i)
    print id(j)

    def __init__(self):
        print id(self.i)
        print id(self.j)
        self.i += 1
        self.j.append(1)
        print id(self.i)
        print id(self.j)

Bar()

"""
140435923163024
4406753688
140435923163024
4406753688
140435923163000
4406753688
"""
这是因为python的传值特性(call by sharing) 如果你清楚id函数代表的是什么意思的话,你可以清晰的看出,修改i的值时,会改变其id,修改j的值其id始终不变 其实对所有不可变类型和可变类型,都是一样的,这也是python区别于call by value 和call by reference的一个特性
十分感谢您的帮助。阁下从地址层面进行分析问题的方法对我颇有启发,目前对于该问题已经有了一定的理解,也知道该如何解决了。十分感谢!
  • 打赏
  • 举报
回复
qingfeng__ 2017-11-23
引用 4 楼 extend 的回复:
楼上说的对,就是类变量和实例变量的问题。但是和不可变对象/可变对象没关系。 >>> class T(): __h=0 def __init__(self): self.__h=self.__h+1 print(self.__h) print(T.__h) >>> t1=T() 1 #实例变量 0 #类变量 ==========比较下面这个例子======== >>> class T(): __h=0 def __init__(self): T.__h=T.__h+1 print(self.__h) print(T.__h) >>> t3=T() 1 #实例变量 1 #类变量 要实现类实例的计数,把计数器定义在类变量中: >>> class T(): __h=0 def __init__(self): T.__h=T.__h+1 #print(self.__h) print(T.__h) >>> save=[] >>> for i in range(0,10): save.append(T()) 1 2 3 4 5 6 7 8 9 10
感谢您的帮助,结合混沌鳄鱼的回答和您的回答理解后,已解决该问题。不过,我查看了下面这篇文章后,感觉和“不可变对象/可变对象”应该有点关系,虽然可能不大。 具体地址:http://blog.csdn.net/taohuaxinmu123/article/details/39008281
  • 打赏
  • 举报
回复
qingfeng__ 2017-11-23
引用 3 楼 xpresslink 的回复:
楼主这个问题涉及到了Python两个方面的概念 (1)类变量和实例变量的概念 (2)不可变对象和可变对象的概念。 建议楼主分别百度一下python 的这个两个概念,实际上只要你认真看书的话,基本上每本Python书都有讲解。 你在__init__() 中, self.__h = self.__h + 1 实际上,前后两个self.__h 是不同的, 前一个是要声明的实例变量(属性), 后一个则是引用了从类继承来的类变量(属性)。 一个对象的属性查找顺序遵循首先查找实例对象自己,然后是类,接着是类的父类。 通常每个对象都会有一个__dict__属性,是个字典,用来存放对象的属性和值 举栗子:

>>> class T():
	__h = 0
	def __init__(self):
		self.__h = self.__h + 1
		print(self.__h)
>>> T.__dict__
mappingproxy({'__module__': '__main__', '_T__h': 0, '__init__': <function T.__init__ at 0x0000000001D33E18>, '__dict__': <attribute '__dict__' of 'T' objects>, '__weakref__': <attribute '__weakref__' of 'T' objects>, '__doc__': None})
>>> id(T._T__h)
1499448976
>>> t1 = T()
1
>>> t1.__dict__
{'_T__h': 1}
>>> T._T__h
0
>>> T._T__h = 5
>>> t2 = T()
6
>>> t2._T__h
6
>>> 

那么要实现期望的那种每新建一个实例,实现变量值累加也是可以的。 例如楼上同学直接使用类名引用类变量来做所有实例共享累加。 另外一个方法,就是要利用可变对象传引用,可以原地修改值的特性。 再来一个例子

>>> class T:
	h = [0]
	def __init__(self):
		self.h[0] = self.h[0] + 1
		print(self.h[0])

		
>>> id(T.h)
48528136
>>> t1 = T()
1
>>> id(t1.h)
48528136
>>> t2 = T()
2
>>> id(t2.h)
48528136
>>> t3 = T()
3
>>> id(t3.h)
48528136
>>> 

十分感谢您的帮助,阁下就python方面想必学识渊博,对很多东西的理解都十分透彻。您的解答: “你在__init__() 中, self.__h = self.__h + 1 实际上,前后两个self.__h 是不同的, 前一个是要声明的实例变量(属性), 后一个则是引用了从类继承来的类变量(属性)。 一个对象的属性查找顺序遵循首先查找实例对象自己,然后是类,接着是类的父类。 通常每个对象都会有一个__dict__属性,是个字典,用来存放对象的属性和值” 加上两个栗子对我的帮助十分巨大,查阅您所说的两个知识点的内容后再来理解发现确实符合这个事实。简单来说我直接运用append()方法,使得没有产生地址的更改,所以python在处理上并未替我的实例产生相应的实例变量,仍然沿用了类的变量,有点写时复制的意思。感谢您的帮助!
  • 打赏
  • 举报
回复
QuantumEnergy 2017-11-22
另外,如果你有兴趣,你可以看看下面的代码
class Bar(object):
    i = 0
    j = []

    def __init__(self):
        Bar.i += 1
        print Bar.i

for i in range(5):
    Bar()
1 2 3 4 5
  • 打赏
  • 举报
回复
QuantumEnergy 2017-11-22
class Bar(object):
    i = 0
    j = []
    print id(i)
    print id(j)

    def __init__(self):
        print id(self.i)
        print id(self.j)
        self.i += 1
        self.j.append(1)
        print id(self.i)
        print id(self.j)

Bar()

"""
140435923163024
4406753688
140435923163024
4406753688
140435923163000
4406753688
"""
这是因为python的传值特性(call by sharing) 如果你清楚id函数代表的是什么意思的话,你可以清晰的看出,修改i的值时,会改变其id,修改j的值其id始终不变 其实对所有不可变类型和可变类型,都是一样的,这也是python区别于call by value 和call by reference的一个特性
  • 打赏
  • 举报
回复
相关推荐
发帖
脚本语言
加入

3.7w+

社区成员

JavaScript,VBScript,AngleScript,ActionScript,Shell,Perl,Ruby,Lua,Tcl,Scala,MaxScript 等脚本语言交流。
申请成为版主
帖子事件
创建了帖子
2017-11-22 10:12
社区公告

CSDN 脚本语言社区接受专栏投稿(专栏会在顶部创建专属你的栏目),投稿需满足以下要求:

  • 脚本语言技术相关;
  • 文章持续更新,保持活跃;
  • 内容清晰明了,干货为主;
  • 文章排版有序,有条有理。

本社区开通招聘专栏,发布招聘信息请联系版主,发布者需要保证招聘信息真实有效,CSDN 平台和版主不对招聘内容负责!

联系方式:私聊版主、发送邮件、QQ联系等均可: