1.python 面向对象

 
1.__init__() 创建对象时的初始化方法

__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()方法中的调用。

#3楼 2012-12-27 17:27 next163 
self相当于 *this
__init__() 差不多算是构造。
 

#4楼 2013-02-08 15:37 峻祁连 

非常好的教程!,感谢!

不过有一句不太明白,您提到 : (在方法中更改类变量属性的值是危险的,这样会影响根据这个类定义的所有对象的这一属性!!)

我做了个实验:

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啊? 能详细解释一下吗?

 

#7楼[楼主] 2013-02-16 16:41 Vamei 

@峻祁连
你的这种写法可行,是因为你的属性都是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属性的复制机制。

 

#9楼 2013-02-21 15:55 egger 

@峻祁连
不清楚Python的对象内存怎么分配的
C#的类(对象)的不同实例会开辟出新的内存,不同实例的相同属性内存不一样的
以此类推:你修改了Jerry的age这个属性的值,不会影响到Daniel 的age属性 两个age只是同名 但是地址不一样 没有必然联系

#10楼[楼主] 2013-02-21 22:12 Vamei 

@egger
具体的内存实现我没有查。
在Python中, 类的属性和对象的属性是两个概念,尽管在@峻祁连举出的例子中,Python(有时)会自动创建与类属性同名的对象属性,这有点让人混淆。
我觉得好的习惯是遵循简单的原则:使用__init__初始化对象属性值是好习惯

#11楼 2013-02-23 21:57 pang18a 

@egger
http://blog.csdn.net/hsuxu/article/details/7785835 
我也对这里有点迷惑 后来百度了下
大概的意思好像是说 如果是个可变类型的变量 那么赋值时传递的是引用修改的是同一块内存 如果是不可变类型的变量 赋值时 会重新开辟一块内存

#12楼[楼主] 2013-02-24 09:46 Vamei 

@pang18a
对,是这样的。

#13楼 2013-06-18 16:38 MeT 

(通过对象来修改类属性是危险的,这样可能会影响根据这个类定义的所有对象的这一属性!!)
关于这一句,情况是这样的:
1、对象不能修改类的属性,只能修改自己的,也就是说,修改了之后对同类的其他对象没有影响;
2、动态修改类属性可以用类名.属性 = xxx来进行修改;
3、修改的类属性一般会影响所辖对象的属性,除非对象在此之前对该属性进行过修改。

#14楼 2014-02-12 12:45 helloray 

@MeT 
这才是正解

#15楼 2014-02-17 14:03 特务小强 

immutable这是基础知识了。
 

#17楼 2014-03-20 17:07 yexuan910812 

所谓的类成员和对象成员,是不是就是就是Java中static成员与对象成员的区别?

#18楼 2014-03-27 14:41 Triangle23 

最后一个例子中定义的Human类,类中的gender不需要声明吗?
 

#20楼 2014-04-22 11:58 最真的梦 

python 类在实例化的时候,如果成员变量的为引用类型,那么实例化的属性值均为同样的引用值;
可以理解为,class在加载的时候,引用类型的数据就已经在内存块里了;
而在java类实例化的时候,会在内存中开辟一块新的内存,再赋值引用类型的给成员变量。

#21楼 2014-04-22 15:15 Seandor 

恩,list immutable,不能乱改。
 
 

2.特殊方法与多范式

作者: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开发中,你有可能使用这种方法来更合理的管理对象的属性。

#2楼 2012-12-12 08:55 zhuangzhuang1988 
不错不错不错,很好很好
查了下还有这种方法

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

#3楼[楼主] 2012-12-12 09:13 Vamei 

@zhuangzhuang1988
这个就是一种property的用法,只是用了decorator.
#1楼 2012-11-20 09:41 Chenkun 
写的很好,支持一下!
另外:楼主用的是哪个版本的python, 文中的属性小节中__getattr__应该是__getattribute__吧, 通常我们使用的时候会封装一下, 如下:

1
2
3
class student(object):
    def __getattr__(self, name):
        return object.__getattribute__(name)
#2楼 2012-11-20 10:21 zhuangzhuang1988 
@Chenkun
我也是的 没看到..
#3楼 2012-11-20 10:33 Chenkun 
@zhuangzhuang1988
我又去翻了一下官方的文档, 在3.4.2. Customizing attribute access 节中有对__getattr__的解释 :)
#4楼[楼主] 2012-11-20 10:42 Vamei 
@Chenkun
我把它想简单了。等我再读一下官方文档看怎么修改,谢谢。
#5楼[楼主] 2012-11-20 10:59 Vamei 
@Chenkun
@zhuangzhuang1988
我的原意是用__dict__,但是由于还没有提到过__dict__,所以用了__getattr__,但看来是用错了。

整个过程是先用__dict__搜查属性,如果没有,向上找__base__的属性。如果整个树的没有,那么调用__getattr__来生成属性。所以说__dict__和__getattr__是相互配合工作的关系。
而__getattribute__则是无条件的返回属性,无论这一属性是否存在,所以比较“暴力”。

我参考了
http://stackoverflow.com/questions/4295678/understanding-the-difference-between-getattr-and-getattribute

谢谢你们的提醒。

python 面向对象、特殊方法与多范式、对象的属性及与其他语言的差异的更多相关文章

  1. Python帮助函数调试函数 用于获取对象的属性及属性值

    Python帮助函数调试函数 用于获取对象的属性及属性值 刚接触Python,上篇 <Python入门>第一个Python Web程序--简单的Web服务器 中调试非常不方便,不知道对象详 ...

  2. python 面向对象之封装与类与对象

    封装 一,引子 从封装本身的意思去理解,封装就好像是拿来一个麻袋,把小猫,小狗,小王八,小老虎一起装进麻袋,然后把麻袋封上口子.照这种逻辑看,封装='隐藏',这种理解是相当片面的 二,先看如何隐藏 在 ...

  3. Python面向对象 -- 继承和多态、获取对象信息、实例属性和类属性

    继承和多态 继承的好处: 1,子类可以使用父类的全部功能 2,多态:当子类和父类都存在相同的方法时,子类的方法会覆盖父类的方法,即调用时会调用子类的方法.这就是继承的另一个好处:多态. 多态: 调用方 ...

  4. Python 面向对象 特殊方法(魔法方法)

    Python 的特殊方法,两边带双下划线的方法. 比如:__init__(self, ...).__del__(self) 1.__init__(self,...) : 构造方法 __init__(s ...

  5. [Python]python面向对象 __new__方法及单例设计

    __new__ 方法 使用 类名() 创建对象时,Python 的解释器 首先 会 调用 __new__ 方法为对象 分配空间 __new__ 是一个 由 object 基类提供的 内置的静态方法,主 ...

  6. Python面向对象-定制方法

    Python中的class可以定义许多定制方法,可以让我们方便的生成特定的类. 我们之前介绍了__slots__.__len__(),python中还有许多这样的特殊函数: __str__ >& ...

  7. 026.Python面向对象类的相关操作以及对象和类的删除操作

    类的相关操作 定义的类访问共有成员的成员和方法 定义的类动态添加公有成员的属性和方法 定义的类删除公有成员的属性和方法 1 定义一个基本的类 #定义一个类 class Plane(): #添加一个共有 ...

  8. [Web 前端 ] Jquery attr()方法 获取或修改 对象的属性值

    cp from : https://blog.csdn.net/gf771115/article/details/18086707 jquery中用attr()方法来获取和设置元素属性,attr是at ...

  9. python面向对象魔术方法补充

    一.描述符 在 面向对象 编程中 定义一个(没有定义方法)类:class person , 在这个类里面,有name,age, heigth, weight,等等属性, 这个类就可以看作一个对 per ...

随机推荐

  1. Win2008或IIS7的文件上传大小限制解决方案

    默认情况下,IIS7的上传限制为200K.当上传文件小于30M时,可以通过如下方法设置:在iis7中找到asp设置,在“asp”的“限制属性”中最后一行“最大请求主体限制”,修改该值为你所想要的,如2 ...

  2. Ping命令与ICMP协议

    ICMP协议 ICMP是"Internet Control Message Ptotocol"(Internet控制消息协议)的缩写.它是TCP/IP协议族的一个子协议,用于在IP ...

  3. 怎样对CODESOFT中的条形码进行黑白转换

      CODESOFT 2015标签设计软件能 够提供无与伦比的灵活性.功能和支持,其面对的用户也是极其的广泛.对于一些需要打印黑白反转条形码的特殊用户,例如使用黑色标签纸的用 户,CODESOFT 2 ...

  4. Android开发-API指南-<uses-library>

    <uses-library> 英文原文:http://developer.android.com/guide/topics/manifest/uses-library-element.ht ...

  5. 分享一个MVC的多层架构,欢迎大家拍砖斧正

    如果你对项目管理.系统架构有兴趣,请加微信订阅号"softjg",加入这个PM.架构师的大家庭 多层架构是开发人员在开发过程当中面对复杂且易变的需求采取的一种以隔离控制为主的应对策 ...

  6. VS/Visual studio 源代码编辑器里的空处出现点号解决办法

    此原因是不小心按错了键盘上的组合键Ctr+E+S, 再次按Ctr+E+S可消除.

  7. MFC中release版本和debug版本区别

    最近MFC写了个程序,生成release版,原来正常,后来删掉了些控件再编译运行,结果竟然报内存读写错误,debug却是正常的.后来将“Project   Settings”   中   “C++/C ...

  8. Python 编程规范-----转载

    Python编程规范及性能优化 Ptyhon编程规范 编码 所有的 Python 脚本文件都应在文件头标上 # -*- coding:utf-8 -*- .设置编辑器,默认保存为 utf-8 格式. ...

  9. 使用/proc实现内核与用户空间通信

    1. 前言   Linux内核空间与用户空间的通信可通过"/proc"目录的文件读写来实现,如果只是控制内核中的参数而不是传输较多数据的话,用“/proc”是很合适的.另外一种内核 ...

  10. Android knock code analysis

    My colleague she forgot the knock code and ask me for help. I know her phone is LG G3 D855 with Andr ...