1.python 面向对象
__init__()是一个特殊方法(special method)。Python有一些特殊方法。Python会特殊的对待它们。特殊方法的特点是名字前后有两个下划线。
如果你在类中定义了__init__()这个方法,创建对象时,Python会自动调用这个方法。这个过程也叫初始化。
class happyBird(Bird):
def __init__(self,more_words):
print 'We are happy birds.',more_words
summer = happyBird('Happy,Happy!')
这里继承了Bird类,它的定义见上一讲。
屏幕上打印:
We are happy birds.Happy,Happy!
我们看到,尽管我们只是创建了summer对象,但__init__()方法被自动调用了。最后一行的语句(summer = happyBird...)先创建了对象,然后执行:
summer.__init__(more_words)
'Happy,Happy!' 被传递给了__init__()的参数more_words
2.类的属性
上一讲中提到,在定义方法时,必须有self这一参数。这个参数表示某个对象。对象拥有类的所有性质,那么我们可以通过self,调用类属性。
class Human(object):
laugh = 'hahahaha'
def show_laugh(self):
print self.laugh
def laugh_100th(self):
for i in range(100):
self.show_laugh() li_lei = Human()
li_lei.laugh_100th()
这里有一个类属性laugh。在方法show_laugh()中,通过self.laugh,调用了该属性的值。
还可以用相同的方式调用其它方法。方法show_laugh(),在方法laugh_100th中()被调用。
通过对象可以修改类属性值(引用类型)。但这是危险的。类属性(引用类型)被所有同一类及其子类的对象共享。类属性(引用类型)值的改变会影响所有的对象。
3.对象的属性(性质)
我们讲到了许多属性,但这些属性是类的属性。所有属于该类的对象会共享这些属性。比如说,鸟都有羽毛,鸡都不会飞。
在一些情况下,我们定义对象的性质,用于记录该对象的特别信息。比如说,人这个类。性别是某个人的一个性质,不是所有的人类都是男,或者都是女。这个性质的值随着对象的不同而不同。李雷是人类的一个对象,性别是男;韩美美也是人类的一个对象,性别是女。
当定义类的方法时,必须要传递一个self的参数。这个参数指代的就是类的一个对象。我们可以通过操纵self,来修改某个对象的性质。比如用类来新建一个对象,即下面例子中的li_lei, 那么li_lei就被self表示。我们通过赋值给self.attribute,给li_lei这一对象增加一些性质,比如说性别的男女。self会传递给各个方法。在方法内部,可以通过引用self.attribute,查询或修改对象的性质。
这样,在类属性的之外,又给每个对象增添了各自特色的性质,从而能描述多样的世界。
class Human(object):
def __init__(self, input_gender):
self.gender = input_gender
def printGender(self):
print self.gender li_lei = Human('male') # 这里,'male'作为参数传递给__init__()方法的input_gender变量。
print li_lei.gender
li_lei.printGender()
在初始化中,将参数input_gender,赋值给对象的性质,即self.gender。
li_lei拥有了对象性质gender。gender不是一个类属性。Python在建立了li_lei这一对象之后,使用li_lei.gender这一对象性质,专门储存属于对象li_lei的特有信息。
对象的性质也可以被其它方法调用,调用方法与类属性的调用相似,正如在printGender()方法中的调用。
不过有一句不太明白,您提到 : (在方法中更改类变量属性的值是危险的,这样会影响根据这个类定义的所有对象的这一属性!!)
我做了个实验:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
class Human( object ):
Can_Talk = True
Can_Walk = True
Age = 0
Name = ""
def Say( self , msg):
print "I am saying: " + msg
class Child(Human):
def Cry( self ):
print "wa wa ...."
def ShowAge( self ):
print self .Name, " is " , self .Age , " years old."
def Grow( self , yr) :
self .Age = yr
Jerry = Child()
Jerry.Name = "Jerry"
Jerry.Age = 3
Jerry.Grow( 4 )
Jerry.ShowAge() Daniel = Child()
Daniel.Name = "Daniel"
Daniel.Grow( 1 )
Daniel.ShowAge() |
输出结果:
C:\Python27>python c:\if.py
Jerry is 4 years old.
Daniel is 1 years old.
我在Jerry实例的Grow()方法中更改了类变量属性Age的值,没发现影响到其他对象Daniel啊? 能详细解释一下吗?
你的这种写法可行,是因为你的属性都是immutable的(比如整数、字符串)。但如果属性是mutable的话(比如list),就会出现问题。比如下面的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class Human( object ):
Can_Talk = True
Can_Walk = True
Age = 0
Name = [ "Li" , "Lei" ]
a = Human()
b = Human()
a.Age + = 1
print a.Age
print b.Age
a.Name[ 0 ] = "Wang"
print a.Name
print b.Name
|
为什么immutable是可行的呢?原因是,在更改对象属性时,如果属性是immutable的,该属性会被复制出一个副本,存放在对象的__dict__中。你可以通过下面的方式查看:
print a.__class__.__dict__
print a.__dict__
注意到类中和对象中各有一个Age。一个为0, 一个为1。所以我们在查找a.Age的时候,会先查到对象的__dict__的值,也就是1。
但mutable的类属性,在更改属性值时,并不会有新的副本。所以更改会被所有的对象看到。
所以,为了避免混淆,最好总是区分类属性和对象的属性,而不能依赖上述的immutable属性的复制机制。
不清楚Python的对象内存怎么分配的
C#的类(对象)的不同实例会开辟出新的内存,不同实例的相同属性内存不一样的
以此类推:你修改了Jerry的age这个属性的值,不会影响到Daniel 的age属性 两个age只是同名 但是地址不一样 没有必然联系
具体的内存实现我没有查。
在Python中, 类的属性和对象的属性是两个概念,尽管在@峻祁连举出的例子中,Python(有时)会自动创建与类属性同名的对象属性,这有点让人混淆。
我觉得好的习惯是遵循简单的原则:使用__init__初始化对象属性值是好习惯。
http://blog.csdn.net/hsuxu/article/details/7785835
我也对这里有点迷惑 后来百度了下
大概的意思好像是说 如果是个可变类型的变量 那么赋值时传递的是引用修改的是同一块内存 如果是不可变类型的变量 赋值时 会重新开辟一块内存
关于这一句,情况是这样的:
1、对象不能修改类的属性,只能修改自己的,也就是说,修改了之后对同类的其他对象没有影响;
2、动态修改类属性可以用类名.属性 = xxx来进行修改;
3、修改的类属性一般会影响所辖对象的属性,除非对象在此之前对该属性进行过修改。
#17楼 2014-03-20 17:07 yexuan910812
#18楼 2014-03-27 14:41 Triangle23
可以理解为,class在加载的时候,引用类型的数据就已经在内存块里了;
而在java类实例化的时候,会在内存中开辟一块新的内存,再赋值引用类型的给成员变量。
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明。
Python一切皆对象,但同时,Python还是一个多范式语言(multi-paradigm),你不仅可以使用面向对象的方式来编写程序,还可以用面向过程的方式来编写相同功能的程序(还有函数式、声明式等,我们暂不深入)。Python的多范式依赖于Python对象中的特殊方法(special method)。
特殊方法名的前后各有两个下划线。特殊方法又被成为魔法方法(magic method),定义了许多Python语法和表达方式,正如我们在下面的例子中将要看到的。当对象中定义了特殊方法的时候,Python也会对它们有“特殊优待”。比如定义了__init__()方法的类,会在创建对象的时候自动执行__init__()方法中的操作。
(可以通过dir()来查看对象所拥有的特殊方法,比如dir(1))
运算符
Python的运算符是通过调用对象的特殊方法实现的。比如:
'abc' + 'xyz' # 连接字符串
实际执行了如下操作:
'abc'.__add__('xyz')
所以,在Python中,两个对象是否能进行加法运算,首先就要看相应的对象是否有__add__()方法。一旦相应的对象有__add__()方法,即使这个对象从数学上不可加,我们都可以用加法的形式,来表达obj.__add__()所定义的操作。在Python中,运算符起到简化书写的功能,但它依靠特殊方法实现。
Python不强制用户使用面向对象的编程方法。用户可以选择自己喜欢的使用方式(比如选择使用+符号,还是使用更加面向对象的__add__()方法)。特殊方法写起来总是要更费事一点。
尝试下面的操作,看看效果,再想想它的对应运算符
(1.8).__mul__(2.0)
True.__or__(False)
内置函数
与运算符类似,许多内置函数也都是调用对象的特殊方法。比如
len([1,2,3]) # 返回表中元素的总数
实际上做的是
[1,2,3].__len__()
相对与__len__(),内置函数len()也起到了简化书写的作用。
尝试下面的操作,想一下它的对应内置函数
(-1).__abs__()
(2.3).__int__()
表(list)元素引用
下面是我们常见的表元素引用方式
li = [1, 2, 3, 4, 5, 6]
print(li[3])
上面的程序运行到li[3]的时候,Python发现并理解[]符号,然后调用__getitem__()方法。
li = [1, 2, 3, 4, 5, 6]
print(li.__getitem__(3))
尝试看下面的操作,想想它的对应
li.__setitem__(3, 0)
{'a':1, 'b':2}.__delitem__('a')
函数
我们已经说过,在Python中,函数也是一种对象。实际上,任何一个有__call__()特殊方法的对象都被当作是函数。比如下面的例子:
class SampleMore(object):
def __call__(self, a):
return a + 5
add = SampleMore() # A function object
print(add(2)) # Call function
map(add, [2, 4, 5]) # Pass around function object
add为SampleMore类的一个对象,当被调用时,add执行加5的操作。add还可以作为函数对象,被传递给map()函数。
当然,我们还可以使用更“优美”的方式,想想是什么。
总结
对于内置的对象来说(比如整数、表、字符串等),它们所需要的特殊方法都已经在Python中准备好了。而用户自己定义的对象也可以通过增加特殊方法,来实现自定义的语法。特殊方法比较靠近Python的底层,许多Python功能的实现都要依赖于特殊方法。我们将在以后看到更多的例子。
大黄蜂,还是Camaro跑车
Python的许多语法都是基于其面向对象模型的封装。对象模型是Python的骨架,是功能完备、火力强大的大黄蜂。但是Python也提供更加简洁的语法,让你使用不同的编程形态,从而在必要时隐藏一些面向对象的接口。正如我们看到的Camaro跑车,将自己威风的火药库收起来,提供方便人类使用的车门和座椅。
3.对象的属性
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明。谢谢!
Python一切皆对象(object),每个对象都可能有多个属性(attribute)。Python的属性有一套统一的管理方案。
属性的__dict__系统
对象的属性可能来自于其类定义,叫做类属性(class attribute)。类属性可能来自类定义自身,也可能根据类定义继承来的。一个对象的属性还可能是该对象实例定义的,叫做对象属性(object attribute)。
对象的属性储存在对象的__dict__属性中。__dict__为一个词典,键为属性名,对应的值为属性本身。我们看下面的类和对象。chicken类继承自bird类,而summer为chicken类的一个对象。
class bird(object):
feather = True class chicken(bird):
fly = False
def __init__(self, age):
self.age = age summer = chicken(2) print(bird.__dict__)
print(chicken.__dict__)
print(summer.__dict__)
下面为我们的输出结果:
{'__dict__': <attribute '__dict__' of 'bird' objects>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'bird' objects>, 'feather': True, '__doc__': None}
{'fly': False, '__module__': '__main__', '__doc__': None, '__init__': <function __init__ at 0x2b91db476d70>}
{'age': 2}
第一行为bird类的属性,比如feather。第二行为chicken类的属性,比如fly和__init__方法。第三行为summer对象的属性,也就是age。有一些属性,比如__doc__,并不是由我们定义的,而是由Python自动生成。此外,bird类也有父类,是object类(正如我们的bird定义,class bird(object))。这个object类是Python中所有类的父类。
可以看到,Python中的属性是分层定义的,比如这里分为object/bird/chicken/summer这四层。当我们需要调用某个属性的时候,Python会一层层向上遍历,直到找到那个属性。(某个属性可能出现再不同的层被重复定义,Python向上的过程中,会选取先遇到的那一个,也就是比较低层的属性定义)。
当我们有一个summer对象的时候,分别查询summer对象、chicken类、bird类以及object类的属性,就可以知道summer对象所有的__dict__,就可以找到通过对象summer可以调用和修改的所有属性了。下面两种属性修改方法等效:
summer.__dict__['age'] = 3
print(summer.__dict__['age']) summer.age = 5
print(summer.age)
(上面的情况中,我们已经知道了summer对象的类为chicken,而chicken类的父类为bird。如果只有一个对象,而不知道它的类以及其他信息的时候,我们可以利用__class__属性找到对象的类,然后调用类的__base__属性来查询父类)
特性
同一个对象的不同属性之间可能存在依赖关系。当某个属性被修改时,我们希望依赖于该属性的其他属性也同时变化。这时,我们不能通过__dict__的方式来静态的储存属性。Python提供了多种即时生成属性的方法。其中一种称为特性(property)。特性是特殊的属性。比如我们为chicken类增加一个特性adult。当对象的age超过1时,adult为True;否则为False:
class bird(object):
feather = True class chicken(bird):
fly = False
def __init__(self, age):
self.age = age
def getAdult(self):
if self.age > 1.0: return True
else: return False
adult = property(getAdult) # property is built-in summer = chicken(2) print(summer.adult)
summer.age = 0.5
print(summer.adult)
特性使用内置函数property()来创建。property()最多可以加载四个参数。前三个参数为函数,分别用于处理查询特性、修改特性、删除特性。最后一个参数为特性的文档,可以为一个字符串,起说明作用。
我们使用下面一个例子进一步说明:
class num(object):
def __init__(self, value):
self.value = value
def getNeg(self):
return -self.value
def setNeg(self, value):
self.value = -value
def delNeg(self):
print("value also deleted")
del self.value
neg = property(getNeg, setNeg, delNeg, "I'm negative") x = num(1.1)
print(x.neg)
x.neg = -22
print(x.value)
print(num.neg.__doc__)
del x.neg
上面的num为一个数字,而neg为一个特性,用来表示数字的负数。当一个数字确定的时候,它的负数总是确定的;而当我们修改一个数的负数时,它本身的值也应该变化。这两点由getNeg和setNeg来实现。而delNeg表示的是,如果删除特性neg,那么应该执行的操作是删除属性value。property()的最后一个参数("I'm negative")为特性negative的说明文档。
使用特殊方法__getattr__
我们可以用__getattr__(self, name)来查询即时生成的属性。当我们查询一个属性时,如果通过__dict__方法无法找到该属性,那么Python会调用对象的__getattr__方法,来即时生成该属性。比如:
class bird(object):
feather = True class chicken(bird):
fly = False
def __init__(self, age):
self.age = age
def __getattr__(self, name):
if name == 'adult':
if self.age > 1.0: return True
else: return False
else: raise AttributeError(name) summer = chicken(2) print(summer.adult)
summer.age = 0.5
print(summer.adult) print(summer.male)
每个特性需要有自己的处理函数,而__getattr__可以将所有的即时生成属性放在同一个函数中处理。__getattr__可以根据函数名区别处理不同的属性。比如上面我们查询属性名male的时候,raise AttributeError。
(Python中还有一个__getattribute__特殊方法,用于查询任意属性。__getattr__只能用来查询不在__dict__系统中的属性)
__setattr__(self, name, value)和__delattr__(self, name)可用于修改和删除属性。它们的应用面更广,可用于任意属性。
即时生成属性的其他方式
即时生成属性还可以使用其他的方式,比如descriptor(descriptor类实际上是property()函数的底层,property()实际上创建了一个该类的对象)。有兴趣可以进一步查阅。
总结
__dict__分层存储属性。每一层的__dict__只存储该层新增的属性。子类不需要重复存储父类中的属性。
即时生成属性是值得了解的概念。在Python开发中,你有可能使用这种方法来更合理的管理对象的属性。
查了下还有这种方法
1
2
3
4
5
6
7
|
class C( object ):
@property
def x( self ): return self ._x
@x .setter
def x( self , value): self ._x = value
@x .deleter
def x( self ): del self ._x
|
另外:楼主用的是哪个版本的python, 文中的属性小节中__getattr__应该是__getattribute__吧, 通常我们使用的时候会封装一下, 如下:
1
2
3
|
class student( object ):
def __getattr__( self , name):
return object .__getattribute__(name)
|
我也是的 没看到..
我又去翻了一下官方的文档, 在3.4.2. Customizing attribute access 节中有对__getattr__的解释 :)
@zhuangzhuang1988
我的原意是用__dict__,但是由于还没有提到过__dict__,所以用了__getattr__,但看来是用错了。
整个过程是先用__dict__搜查属性,如果没有,向上找__base__的属性。如果整个树的没有,那么调用__getattr__来生成属性。所以说__dict__和__getattr__是相互配合工作的关系。
而__getattribute__则是无条件的返回属性,无论这一属性是否存在,所以比较“暴力”。
我参考了
http://*.com/questions/4295678/understanding-the-difference-between-getattr-and-getattribute
谢谢你们的提醒。