Python进阶笔记(4)_ 面向对象编程基础

时间:2022-12-20 14:03:08


根据操作数据的函数或语句块来设计程序的。这被称为面向过程的编程。

还有一种把数据和功能结合起来,用称为对象的东西包裹起来组织程序的方法。这种方法称为面向对象的编程理念。

类和对象是面向对象编程的两个主要方面。类创建一个新类型,而对象是这个类的实例。    

对象可以使用属于对象或类的变量存储数据。这样的变量被称为域。因此,域有两种类型——实例属性和类属性,前者属于每个实例/类的对象,后者属于类本身。关于这个知识点,我们将在下文“类属性和实例属性”一节中讲解。

【注】不同文献将属性称为变量,实例称为对象。所以会看到 对象的变量和类的变量等描述。

    

对象也可以使用属于类的函数来具有功能。这样的函数被称为类的方法。

这些术语帮助我们把它们与孤立的变量函数区分开来。域和方法可以合称为类的属性。

类使用 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
Student
这里,我们看到除了接受 name、gender 和 birth 外,还可接受任意关键字参数,比如 job,并把他们都作为属性赋值给实例。

访问限制

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'

类属性和实例属性

我们通过具体例子来认识类属性和实例属性。

Python进阶笔记(4)_ 面向对象编程基础

输出为

Python进阶笔记(4)_ 面向对象编程基础

这里, population属于Person类,因此是一个类属性。name变量属于对象(它使用 self 赋值)因此是实例属性。可以看到,population 是累加的。

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

好,问题来了,怎么能让两个相同的类具有不同的类属性呢?

看下例就一目了然了。

Python进阶笔记(4)_ 面向对象编程基础

输出

Python进阶笔记(4)_ 面向对象编程基础

这里P.address = England 就将 P.address 命令为一个实例属性。这里只改变了 P 的类属性,而Person 的类属性并没有改变。可见,当实例属性和类属性重名时,实例属性优先级高,它将屏蔽掉对类属性访问。

若要消除的实例属性后,再访问P.address 就返回原来 Person 的类属性值了。

del P.address
print P.address
输出 China 。


方法


定义实例方法
一个实例的私有属性就是以__开头的属性,无法被外部访问,那这些属性定义有什么用?
虽然私有属性无法从外部访问,但是,从类的内部是可以访问的。除了可以定义实例的属性外,还可以定义实例的方法。
实例的方法就是在类中定义的函数,它的第一个参数永远是 self,指向调用该方法的实例本身,其他参数和一个普通函数是完全一样的:

class Person(object):

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()
get_score(self) 就是一个实例方法,它的第一个参数是self。__init__(self, name)其实也可看做是一个特殊的实例方法。输出为

Phoenix is good
None
Tree is excellent
None
Sky is need work harder
None

在实例方法内部,可以访问所有实例属性,这样,如果外部需要访问私有属性,可以通过方法调用获得,这种数据封装的形式除了能保护内部数据一致性外,还可以简化外部调用的难度。


将函数定义为方法

我们在 class 中定义的实例方法其实也是属性,它实际上是一个函数对象。因为方法也是一个属性,所以,它也可以动态地添加到实例上,只是需要用 types.MethodType() 把一个函数变为一个方法:

Python进阶笔记(4)_ 面向对象编程基础
输出为:

Python进阶笔记(4)_ 面向对象编程基础

出错原因在于 p2 实例并没有绑定 get_grade。

类方法

和属性类似,方法也分实例方法和类方法。
在class中定义的全部是实例方法,实例方法第一个参数 self 是实例本身。
要在class中定义类方法,需要这么写:

class Person(object):

__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()
输出结果为 1   2
通过标记一个 @classmethod,该方法将绑定到 Person 类上,而非类的实例。类方法的第一个参数将传入类本身,通常将参数名命名为 cls,上面的 cls.count 实际上相当于 Person.count。