根据操作数据的函数或语句块来设计程序的。这被称为面向过程的编程。
还有一种把数据和功能结合起来,用称为对象的东西包裹起来组织程序的方法。这种方法称为面向对象的编程理念。
类和对象是面向对象编程的两个主要方面。类创建一个新类型,而对象是这个类的实例。对象可以使用属于对象或类的变量存储数据。这样的变量被称为域。因此,域有两种类型——实例属性和类属性,前者属于每个实例/类的对象,后者属于类本身。关于这个知识点,我们将在下文“类属性和实例属性”一节中讲解。
【注】不同文献将属性称为变量,实例称为对象。所以会看到 对象的变量和类的变量等描述。
对象也可以使用属于类的函数来具有功能。这样的函数被称为类的方法。
这些术语帮助我们把它们与孤立的变量和函数区分开来。域和方法可以合称为类的属性。
类使用 class 关键字创建。类的域和方法被列在一个缩进块中。
域
创建一个类
按照 Python 的编程习惯,类名以大写字母开头,紧接着是(object),表示该类是从哪个类继承下来的。类的继承将在后面的章节讲解,现在我们只需要简单地从object类继承。class People(object):上例的输出为
pass # An empty block
Phoenix = People()
Tree = People()
print Phoenix
print Tree
print Phoenix = = Tree
<__main__.People object at 0x7f771d1414d0>
<__main__.People object at 0x7f771d156850>
False
可以看到,虽然都是用 People 这个类定义的,但二者并不相同,且存储位置也不同。
创建实例属性
由于Python是动态语言,对每一个实例,都可以直接给他们的属性赋值。而且可以赋予不同内容的属性。
class People(object):
pass
Phoenix = People()
Phoenix .name = 'Phoenix'
Phoenix .gender = 'Male'
Phoenix .grade = 3
Tree = People()
Tree .name = 'Tree'
Tree .school = 'No. 2 High School'
Tree .grade = 2
实例的属性可以像普通变量一样进行操作:
Phoenix.grade = Tree.grade + 1
初始化实例属性 __int__()
如果,我们希望定义的类都拥有相同的属性内容,可以用 def __int__() 方法来定义初始值。注意,__int__ 这里是两个下划线 + int + 两个下划线。
class People(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)
Phoenix = People('Phoenix', 'Male', '1995-04-25', job='Student')
print Phoenix.name
print Phoenix.job
输出为
Phoenix这里,我们看到除了接受 name、gender 和 birth 外,还可接受任意关键字参数,比如 job,并把他们都作为属性赋值给实例。
Student
访问限制
Python对属性权限的控制是通过属性名来实现的,如果一个属性由双下划线开头__,该属性就无法被外部访问。
class People(object):上例的输出为
def __init__(self, name, score):
self.name = name
self._gender = 'Male'
self.__score = score
p = People('Phoenix', 97)
print p.name
print p._gender
print p.__score
Phoenix
Male
Traceback (most recent call last):
File "C:/Users/PhoenixTree/Desktop/test.py", line 9, in <module>
print p.__score
AttributeError: 'People' object has no attribute '__score'
类属性和实例属性
我们通过具体例子来认识类属性和实例属性。
输出为
这里, population属于Person类,因此是一个类属性。name变量属于对象(它使用 self 赋值)因此是实例属性。可以看到,population 是累加的。
绑定在一个实例上的属性不会影响其他实例,但是,类本身也是一个实例,如果在类上绑定一个属性,则所有实例都可以访问类的属性,并且,所有实例访问的类属性都是同一个!也就是说,实例属性每个对象各自拥有,互相独立,而类属性有且只有一份。
好,问题来了,怎么能让两个相同的类具有不同的类属性呢?
看下例就一目了然了。
输出
这里P.address = England 就将 P.address 命令为一个实例属性。这里只改变了 P 的类属性,而Person 的类属性并没有改变。可见,当实例属性和类属性重名时,实例属性优先级高,它将屏蔽掉对类属性访问。
若要消除的实例属性后,再访问P.address 就返回原来 Person 的类属性值了。
del P.address输出 China 。
print P.address
方法
定义实例方法
一个实例的私有属性就是以__开头的属性,无法被外部访问,那这些属性定义有什么用?
虽然私有属性无法从外部访问,但是,从类的内部是可以访问的。除了可以定义实例的属性外,还可以定义实例的方法。
实例的方法就是在类中定义的函数,它的第一个参数永远是 self,指向调用该方法的实例本身,其他参数和一个普通函数是完全一样的:
class Person(object):get_score(self) 就是一个实例方法,它的第一个参数是self。__init__(self, name)其实也可看做是一个特殊的实例方法。输出为
def __init__(self, name, score):
self.name = name
self.__score = score
def get_grade(self):
if self.__score == 100:
print self.name + 'is excellent'
elif self.__score > 95:
print self.name + 'is good'
else:
print self.name + 'is need work harder'
p1 = Person('Phoenix', 97)
p2 = Person('Tree', 100)
p3 = Person('Sky', 93)
print p1.get_grade()
print p2.get_grade()
print p3.get_grade()
Phoenix is good
None
Tree is excellent
None
Sky is need work harder
None
在实例方法内部,可以访问所有实例属性,这样,如果外部需要访问私有属性,可以通过方法调用获得,这种数据封装的形式除了能保护内部数据一致性外,还可以简化外部调用的难度。
将函数定义为方法
我们在 class 中定义的实例方法其实也是属性,它实际上是一个函数对象。因为方法也是一个属性,所以,它也可以动态地添加到实例上,只是需要用 types.MethodType() 把一个函数变为一个方法:
输出为:
出错原因在于 p2 实例并没有绑定 get_grade。
类方法
和属性类似,方法也分实例方法和类方法。
在class中定义的全部是实例方法,实例方法第一个参数 self 是实例本身。
要在class中定义类方法,需要这么写:
class Person(object):输出结果为 1 2
__count = 0
@classmethod
def how_many(cls):
return cls.__count
def __init__(self, name):
self.name = name
Person.__count = Person.__count + 1
p1 = Person('Phoenix')
print Person.how_many()
p2 = Person('Tree')
print Person.how_many()
通过标记一个 @classmethod,该方法将绑定到 Person 类上,而非类的实例。类方法的第一个参数将传入类本身,通常将参数名命名为 cls,上面的 cls.count 实际上相当于 Person.count。