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

qingfeng__ 2017-11-22 10:12:23


对于python中一个类的属性的一个问题。为什么当我的一个属性为普通常量时,这个数不随着我类的实例的增加而改变呢?而我的一个属性为列表时,我的列表值却随着实例的增加而产生了改变。(已测试,与属性的私有公有无关。)有大神遇到过这个问题吗?
...全文
252 8 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
好想长大 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的一个特性

37,743

社区成员

发帖
与我相关
我的任务
社区描述
JavaScript,VBScript,AngleScript,ActionScript,Shell,Perl,Ruby,Lua,Tcl,Scala,MaxScript 等脚本语言交流。
社区管理员
  • 脚本语言(Perl/Python)社区
  • WuKongSecurity@BOB
加入社区
  • 近7日
  • 近30日
  • 至今

试试用AI创作助手写篇文章吧