码迷,mamicode.com
首页 > 编程语言 > 详细

Python面向对象-属性

时间:2018-02-03 16:14:22      阅读:181      评论:0      收藏:0      [点我收藏+]

标签:err   怎么办   cal   初学者   job   设置   创建失败   自动调用   recent   

属性

实例属性

虽然可以通过Person类创建出xiaomingxiaohong等实例,但是这些实例看上除了地址不同外,没有什么其他不同。在现实世界中,区分xiaomingxiaohong要依靠他们各自的名字、性别、生日等属性。

In [8]: student.name = 'oneTOinf'

In [9]: student.gender = 'Male'

In [10]: student.birth = '1995-12-24'

In [11]: teacher.name = 'PianoOrRock'

In [12]: teacher.school = 'LiuDu'

In [13]: teacher.age = 33

实例的属性可以像普通变量一样进行操作:

In [14]: teacher.age = 33 + 1

In [15]: teacher.age
Out[15]: 34

示例

创建包含两个 Person 类的实例的 list,并给两个实例的 name 赋值,然后按照 name 进行排序。

In [1]: class Person(object):
   ...:     pass
   ...:

In [2]: p1 = Person()

In [3]: p1.name = 'Bart'

In [4]: p2 = Person()

In [5]: p2.name = 'Adam'

In [6]: p3 = Person()

In [7]: p3.name = 'Lisa'

In [8]: L1 = [p1, p2, p3]

In [9]: L2 = sorted(L1, lambda p1, p2: cmp(p1.name, p2.name))

In [10]: print L2[0].name
Adam

In [11]: print L2[1].name
Bart

In [12]: print L2[2].name
Lisa

初始化

虽然我们可以自由地给一个实例绑定各种属性,但是,现实世界中,一种类型的实例应该拥有相同名字的属性。例如,Person类应该在创建的时候就拥有 namegenderbirth 属性,怎么办?

在定义 Person 类时,可以为Person类添加一个特殊的__init__()方法,当创建实例时,__init__()方法被自动调用,我们就能在此为每个实例都统一加上以下属性:

class Person(object):
    def __init__(self, name, gender, birth):
        self.name = name
        self.gender = gender
        self.birth = birth

__init__() 方法的第一个参数必须是 self(也可以用别的名字,但建议使用习惯用法),后续参数则可以自由指定,和定义函数没有任何区别。相应地,创建实例时,就必须要提供除 self 以外的参数:

xiaoming = Person('Xiao Ming', 'Male', '1991-1-1')
xiaohong = Person('Xiao Hong', 'Female', '1992-2-2')

有了__init__()方法,每个Person实例在创建时,都会有 namegenderbirth 这3个属性,并且,被赋予不同的属性值,访问属性使用.操作符:

print xiaoming.name
# 输出 'Xiao Ming'
print xiaohong.birth
# 输出 '1992-2-2'

要特别注意的是,初学者定义__init__()方法常常忘记了 self 参数:

>>> class Person(object):
...     def __init__(name, gender, birth):
...         pass
... 
>>> xiaoming = Person('Xiao Ming', 'Male', '1990-1-1')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __init__() takes exactly 3 arguments (4 given)

这会导致创建失败或运行不正常,因为第一个参数namePython解释器传入了实例的引用,从而导致整个方法的调用参数位置全部没有对上。

示例

定义Person类的__init__方法,除了接受 namegenderbirth 外,还可接受任意关键字参数,并把他们都作为属性赋值给实例。要定义关键字参数,使用 **kw;除了可以直接使用self.name = ‘xxx‘设置一个属性外,还可以通过 setattr(self, ‘name‘, ‘xxx‘)设置属性。

class Person(object):
    def __init__(self, name, gender, birth, **kw):
        self.name = name
        self.gender = gender
        self.birth = birth
        for k, v in kw.iteritems():
            setattr(self, k, v)
xiaoming = Person('Xiao Ming', 'Male', '1990-1-1', job='Student')
print xiaoming.name
print xiaoming.job

类属性

类是模板,而实例则是根据类创建的对象。

绑定在一个实例上的属性不会影响其他实例,但是,类本身也是一个对象,如果在类上绑定一个属性,则所有实例都可以访问类的属性,并且,所有实例访问的类属性都是同一个!也就是说,实例属性每个实例各自拥有,互相独立,而类属性有且只有一份。

定义类属性可以直接在 class 中定义:

class Person(object):
    address = 'Earth'
    def __init__(self, name):
        self.name = name

因为类属性是直接绑定在类上的,所以,访问类属性不需要创建实例,就可以直接访问:

print Person.address
# => Earth

对一个实例调用类的属性也是可以访问的,所有实例都可以访问到它所属的类的属性:

p1 = Person('Bob')
p2 = Person('Alice')
print p1.address
# => Earth
print p2.address
# => Earth

由于Python是动态语言,类属性也是可以动态添加和修改的:

Person.address = 'China'
print p1.address
# => 'China'
print p2.address
# => 'China'

因为类属性只有一份,所以,当Person类的address改变时,所有实例访问到的类属性都改变了。

示例

请给 Person 类添加一个类属性 count,每创建一个实例,count 属性就加 1,这样就可以统计出一共创建了多少个 Person 的实例

由于创建实例必定会调用__init__()方法,所以在这里修改类属性 count 很合适。

参考代码:

class Person(object):
    count = 0
    def __init__(self, name):
        Person.count = Person.count + 1
        self.name = name
p1 = Person('Bob')
print Person.count
# => 1
p2 = Person('Alice')
print Person.count
# => 2
p3 = Person('Tim')
print Person.count
# => 3

实例属性和类属性冲突

修改类属性会导致所有实例访问到的类属性全部都受影响,但是,如果在实例变量上修改类属性会发生什么问题呢?

class Person(object):
    address = 'Earth'
    def __init__(self, name):
        self.name = name

p1 = Person('Bob')
p2 = Person('Alice')

print 'Person.address = ' + Person.address

p1.address = 'China'
print 'p1.address = ' + p1.address

print 'Person.address = ' + Person.address
print 'p2.address = ' + p2.address

结果如下:

Person.address = Earth
p1.address = China
Person.address = Earth
p2.address = Earth

我们发现,在设置了 p1.address = ‘China‘ 后,p1访问 address 确实变成了 ‘China‘,但是,Person.address和p2.address仍然是‘Earch‘,怎么回事?

原因是 p1.address = ‘China‘并没有改变 Person 的 address,而是给 p1这个实例绑定了实例属性address ,对p1来说,它有一个实例属性address(值是‘China‘),而它所属的类Person也有一个类属性address,所以:

访问 p1.address 时,优先查找实例属性,返回‘China‘。

访问 p2.address 时,p2没有实例属性address,但是有类属性address,因此返回‘Earth‘。

可见,当实例属性和类属性重名时,实例属性优先级高,它将屏蔽掉对类属性的访问。

当我们把 p1 的 address 实例属性删除后,访问 p1.address 就又返回类属性的值 ‘Earth‘了:

del p1.address
print p1.address
# => Earth

可见,千万不要在实例上修改类属性,它实际上并没有修改类属性,而是给实例绑定了一个实例属性。

示例

请把上节的 Person 类属性 count 改为 __count,再试试能否从实例和类访问该属性。

把count改为私有__count,这样实例变量在外部无法修改__count

参考代码:

class Person(object):
    __count = 0
    def __init__(self, name):
        Person.__count = Person.__count + 1
        self.name = name
        print Person.__count

p1 = Person('Bob')
p2 = Person('Alice')

print Person.__count

Python面向对象-属性

标签:err   怎么办   cal   初学者   job   设置   创建失败   自动调用   recent   

原文地址:https://www.cnblogs.com/oneTOinf/p/8409144.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!