我正在学习Python,但没有OOP经验。我在IDLE(Python 3.3)中输入以下行,并且对象的行为让我感到困惑:
>>> class IBAN:
ISOcode = ' '
checkDigits = '00'
class CCC:
bankCode = '0000'
branchCode = '0000'
checkBank = '0'
checkAccount = '0'
account = '0000000000'
>>> >>> numberOne = IBAN()
>>> numberOne.CCC
<class '__main__.IBAN.CCC'>
>>> numberOne.CCC.bankCode
'0000'
>>> numberOne.ISOcode = 'ES'
>>> IBAN.ISOcode
' '
>>> numberOne.ISOcode
'ES'
>>> IBAN.ISOcode = 'FR'
>>> numberOne.ISOcode
'ES'
>>> IBAN.ISOcode = 'FR'
>>> IBAN.ISOcode
'FR'
>>> numberTwo = IBAN()
>>> numberTwo.ISOcode
'FR'
>>> IBAN.ISOcode = 'IT'
>>> IBAN.ISOcode
'IT'
>>> numberOne.ISOcode
'ES'
>>> numberTwo.ISOcode
'IT'
>>> IBAN.ISOcode is numberTwo.ISOcode
True
>>>
为什么对象numberTwo
的ISOcode属性与IBAN
的ISOcode相同,但与对象numberOne
的ISOcode不同?
我期待这样的事情:
>>> numberOne.ISOcode
'ES'
>>> numberTwo.ISOcode
'FR'
然而,这与我的结果不符。
我发现a similar question on StackOverflow,这让我觉得结果应该是:
>>> numberOne.ISOcode
'IT'
>>> numberTwo.ISOcode
'IT'
但这与我的结果不符。
任何人都可以解释或链接到解释Python中Classes行为的资源吗?我试图研究Guido van Rossum的Python教程和“Unifying types and classes in Python 2.2”,但我无法理解他们在说什么。
谢谢!
答案 0 :(得分:4)
该类是与该类实例不同的对象。类具有属性,而实例具有属性。每次尝试访问实例的属性时,如果实例没有该属性,则会在该类上查找该属性。如果在实例上设置属性,它总是在实例上设置,即使这意味着它会影响类上同名属性。
在您的班级IBAN中,您在类上定义了一个属性ISOcode
。执行numberOne.ISOcode = 'ES'
后,在实例上设置 new 属性。您已将numberOne.ISOcode
的值与IBAN.ISOcode
的值相关联。但是您创建的任何新实例(如numberTwo
)仍然没有自己的实例ISOcode。然后,当您设置IBAN.ISOcode
时,您正在更改类属性。
您可以将class属性视为“默认”。当您执行obj.attr
时,如果obj
没有自己的属性attr
,则会获取该类当时的值。如果更改类属性的值,则更改默认值;如果您稍后尝试在没有自己的属性的实例上访问该属性,他们将看到您创建的新默认值。
这是一个更精简的例子:
>>> class Foo(object):
... attr = "Something"
>>> one = Foo()
>>> two = Foo()
首先,所有值都相同,因为我没有指定任何特定于实例的值,因此one
和two
都从类中获取它们的值。
>>> Foo.attr
'Something'
>>> one.attr
'Something'
>>> two.attr
'Something'
现在我将为one
设置一个新值。这不会影响Foo
,two
仍会从Foo
获得其值,因此two
也不受影响。
>>> one.attr = "This is only for #1"
>>> Foo.attr
'Something'
>>> one.attr
'This is only for #1'
>>> two.attr
'Something'
现在我将在课程Foo
上设置一个新值。这不会影响one
,因为one
有自己的值。但是由于two
没有自己的值,它从类中获取它的值,因为我改变了类的值,这改变了我从two
得到的值:
>>> Foo.attr = "This is the class value"
>>> Foo.attr
'This is the class value'
>>> one.attr
'This is only for #1'
>>> two.attr
'This is the class value'
要记住的是,每次执行时都会动态计算每个属性查找。创建实例不会自动将类属性从类复制到实例。当您访问实例上的属性时,实例会对自己说“我有自己的属性吗?如果有,请返回该值。如果没有,则返回类的值。”它每次尝试访问属性时都会询问;实例没有时刻成为班级的“独立”。
答案 1 :(得分:0)
ISOcode
是所谓的类属性,而不是实例属性
您应该在班级self.ISOcode
方法中设置__init__
,以使其对每个实例都是唯一的。否则该属性存储在类中。
class IBAN:
def __init__(self):
self.ISOcode = ' '
答案 2 :(得分:0)
通过在类属性上设置实例属性,您正在做一些令人困惑的名称阴影。
>>> class A():
stuff = ''
>>> a = A()
>>> a.stuff
''
>>> a.stuff = 'thing' # sets an instance attribute
>>> a.stuff
'thing'
>>> a.__class__.stuff
''
>>> a.__class__.stuff = 'blah'
>>> a.stuff # instance attributes are checked FIRST
'thing'
>>> aa = A()
>>> aa.stuff
'blah'
基本上,不要这样做。如果您打算将属性作为实例属性,请在__init__
。
答案 3 :(得分:0)
Python具有类属性和实例属性。当您指定类似
的值时x.ISOcode = 'b'
它为实例创建成员属性。当您首先访问对象中的属性时,它会搜索成员属性,如果没有找到,则会在类属性中搜索。
考虑以下示例
class IBAN(object):
ISOcode = 'a'
print IBAN.ISOcode // outputs a
x = new IBAN()
print x.ISOcode // outputs "a"
x.ISOcode = 'b' // creates a member variable for instance x instead of modifying class variable
print IBAN.ISOcode // outputs "a"
print x.ISOcode // outputs "b"
print x.__class__.ISOcode // outputs "a"
y = new IBAN()
print y.ISOcode // outputs "a"
IBAN.ISOcode = 'c'
print IBAN.ISOcode // outputs "c"
print x.ISOcode // outputs "b" because member variable is set
print x.__class__.ISOcode // outputs "c"
print y.ISOcode // outputs "c" because it still uses class variable. no instance variable set for this instance
答案 4 :(得分:0)
您看到的行为是因为成员访问python对象的回退行为。 numberOne
是IBAN
类的一个实例,当您尝试访问其成员时,例如使用numberOne.ISOCode
,它将检查对象本身是否有一个名为的成员ISOCode
。如果是,它将使用它。如果没有,那么它将回退到对象的类型,并检查该类型是否具有该名称的成员,并使用它。
但是,当您在对象上设置成员时,例如在numberOne.ISOCode = 'ES'
时,它不执行回退行为,它或者使用该对象的现有成员,或者如果没有这样的成员,将创造它。
这可能没关系,因为你之前从未做过OOP,但Python做的事情与其他语言有点不同。你没有在python类中声明实例成员,所以当你直接在ISOCode
类下设置IBAN
时,你就会使它成为{{1}的成员类对象,而不是该类的单个实例。如果您希望类的所有实例都有一个字段,请在该类的IBAN
构造函数中指定它。例如:
__init__
这将确保在创建实例时,它将拥有自己的#!/bin/python
class IBAN:
def __init__(self):
self.ISOCode = ' '
成员。