python学习笔记之面向对象理论基础
面向对象基础理论
基本哲学
-
世界是由对象组成的
-
对象具有运动规律和内部状态
-
对象之间的相互作用和通讯构成世界
对象的特性
-
唯一性:世界上没有完相同的树叶,对象都有唯一块内存地址
-
分类性:分类是对现实世界的抽象
OOP的三大特征
-
继承
-
多态,相同的操作在不同的对象上得到的结果是不一样的
-
封装
面向对象的本质
-
面向对象是对数据和行为的封装
-
但是有时候,数据仅仅是数据,方法仅仅是方法
数据和行为的封装
以“门”这个对象来举例,门有门牌号,有开着的状态,有关闭的状态,让可以有打开的行为,也有关闭的行为。
1 2 3 4 5 6 7 8 9 10
| class door: def __init__(self,number,status): self.number = number self.status = status
def open(self): self.status = 'opening'
def close(self): self.status = 'closed'
|
上边就以面向对象的方式定义了“门”这个对象
1
| door1 = door(1002,'closed')
|
class
是定义一个类的关键字,其语法如下:
等价于:
1 2
| class ClassName(object): pass
|
因为在python中所有的类都继承自object
类。
在类中用def
关键字定义的函数叫做类的方法,所一般函数不同的是,类中方法的第一个参数必须的是self
类只是一个抽象的概念,类不能直接调用,只有初始化后才能对类中的方法进行操作。形象点说类就相当于建造房屋的蓝图,实例化就是修造了一个实实在在的房屋,这个房屋有几间房,各个房间的功用不同,这些就是实例可访问操作的属性或方法。
构造方法
python中构造函数名称为__init__
,以一个例子说明,如下:
1 2 3
| class A: def __init__(self,x): self.x = x
|
上边定义了一个类A,类中只有一个构造方法,接下实例化此类:
5
如上,用“类名.变量名”的方式可以访问类中的变量。当一个类实例化时会运行__init__
这个构造函数,self
表示类实例化后的实例本身,self.x = x
表示把x
这个变量绑定在实例上,并把这个变量指向实例化时传递进来的5
这个对象,这种通过构造函数初始化出来的变量叫做实例变量
。
既然self
表示实例本身,那类没有实例化前,self
从何而来?其实质是在在类中还有一个__new__
方法,如下:
1 2 3 4 5 6 7 8 9 10
| class B: def __new__(cls,*args,**kwargs): print('call __new__') print('cls type is {0}'.format(type(cls))) return object.__new__(cls)
def __init__(self,x): print('call __init__') print('self type is {0}'.format(type(self))) self.x = x
|
call __new__
cls type is <class 'type'>
call __init__
self type is <class '__main__.B'>
从上边的输出可看出,在实例化class B
时,首先调用了__new__
函数,再调用了__init__
这个构造方法,__new__
函数实质是产生了一个实例对象,所以在__init__
函数时可以用self
来代表实例本身。__new__
函数中的cls
代表类本身,当解释器执行到class B:
语句时,类就已经产生了,所以在__new__
函数时就可以使用cls
这个对象。
总结:__new__
函数负责创建实例,__init__
函数只负责初始化实例变量或方法。在实际工作中不建议去修改__new__
函数。
在类中定义的函数的参数中除了第一个参数必须是self
外,和普通的函数参数用法完全相同,也支持默认参数、可变参数。
访问控制
以例子来说明:
1 2 3 4 5 6 7 8 9 10 11 12
| class C: def __init__(self,x): self.__val = x
def __add(self,i): self.__val += i
def get_val(self): return self.__val
def inc(self,k): self.__add(k)
|
5
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-24-edab75c4725a> in <module>()
----> 1 c.__val
AttributeError: 'C' object has no attribute '__val'
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-21-ca5cc7410971> in <module>()
----> 1 c.__add(2)
AttributeError: 'C' object has no attribute '__add'
7
C
这个类实例化后,运行c.get_val()
能正确得到变量__val
的值,但直接用c.__val
访问实例变量抛出了AttributeError
错误,这是因为在构造函数中的__val
这个实例变量名以双下划线开始且不以双下划线结束,这样命名的变量叫私有变量
,此变量只能在类中调用,在类外不可访问。在调用c.__add(2)
时抛出错误同样是因为这是一个私有方法,只在类内部调用,调用c.inc(2)
就是很好的证明。
事实上在python中并没有真正实现对象的私有化,只是把对象的名称进行改名而已,如下:
['_C__add',
'_C__val',
'__class__',
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__gt__',
'__hash__',
'__init__',
'__le__',
'__lt__',
'__module__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__',
'get_val',
'inc']
7
本来访问类变量是c.__val
,python为了让私有变量不让外部访问,把这个私有变量重新命名为“_类名__变量名
”,在实际编程一般不会这样来访问一个私有对象。
类变量
类变量就是定义在类中而在类方法之外的变量,以一个例子来说明:
1 2 3 4
| class D: val = 5 def __init__(self): pass
|
上边的val
就是一个类变量,接下来实例化此类:
5
5
7
5
上边把类D分别进行了实例化,一个是实例d1,另一个是实例d2,当执行d1.val += 2
后,d1实例中的类变量val
的值变为了“7”,而d2实例中的类变量val
依然还是“5”,为什么?先再来看一个例子,如下:
1 2 3 4
| class F: val = [1,2,3] def __init__(self): pass
|
[1, 2, 3]
[1, 2, 3]
[1, 2, 3, 4]
[1, 2, 3, 4]
当执行f1.val.append(4)
后,f1
和f2
两个实例中的"val"变量都发生了改变,为什么在上边D
这个类中只有一个被修改了?
这是因为在实例化后,类变量是各个实例共享的对象,都指向同一个内存地址,起先d1.va1
和d2.val
是指向同一个内存地址,f1.val
和f2.val
是指向同一内存地址,但d1.va1
和d2.val
指向的是一个数字,而f1.val
和f2.val
指向的是一个列表,数字是一个不可变对象
,列表是一个可变对象
,所以在执行d1.val += 2
后,d1.val
新创建了一个指向数字“7”的对象,而d2.val
还是指向实例化时指向数字“5”的对象;而向列表中增加一个元素并不会创建一个新的对象,所以f1.val
和f2.val
依然指向同一个内存地址。
另外,类变量可直接用“类名.变量名”的方式获取,如下:
[1, 2, 3, 4]
再来看一个例子,如下:
1 2 3 4 5
| class G: __val = 3
def get_val(self): return self.__val
|
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-51-8de1b6f555b8> in <module>()
----> 1 G.__val
AttributeError: type object 'G' has no attribute '__val'
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-54-376589fc2b09> in <module>()
----> 1 g1.__val
AttributeError: 'G' object has no attribute '__val'
3
通过以上的代码可看出,类变量以双下划线开始且以非双下划线结束时,类变量就是一个私有有类变量,在类外不能访问。
类方法
类方法用@classmethod
这个装饰器实现,如下:
1 2 3 4 5 6 7 8 9 10
| class H: __val = 3
def __init__(self,x): self.x = x
@classmethod def get_val(cls): print(cls.__val) print(cls.x)
|
如上,因为在实例方法需要传递self
实例本身,类方法需要传递cls
类本身。
3
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-68-0c6296a44132> in <module>()
----> 1 H.get_val()
<ipython-input-66-117847179c76> in get_val(cls)
8 def get_val(cls):
9 print(cls.__val)
---> 10 print(cls.x)
AttributeError: type object 'H' has no attribute 'x'
3
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-69-6f7ca0ed0df3> in <module>()
----> 1 h.get_val()
<ipython-input-66-117847179c76> in get_val(cls)
8 def get_val(cls):
9 print(cls.__val)
---> 10 print(cls.x)
AttributeError: type object 'H' has no attribute 'x'
如上,在执行H.get_val()
和h.get_val()
后都输出了cls__val
这个类私有变量的值,但在访问实例变量时抛出了AttributeError
错误,说明类直接调用,如H.get_val()
也可以通过实例调用,如h.get_val()
,类方法可以访问类私有变量,类方法不可访问实例变量。
静态方法
静态方法使用@staticmethod
这个装饰器来实现,如下:
1 2 3 4 5 6 7 8 9
| class I: __val = 3 val = 4
@staticmethod def print_val(): print(123) print(val) print(__val)
|
123
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-76-26edb3c8e63f> in <module>()
----> 1 i.print_val()
<ipython-input-74-5f0cc11f3b96> in print_val()
6 def print_val():
7 print(123)
----> 8 print(val)
9 print(__val)
NameError: name 'val' is not defined
123
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-77-b5380716c77d> in <module>()
----> 1 I.print_val()
<ipython-input-74-5f0cc11f3b96> in print_val()
6 def print_val():
7 print(123)
----> 8 print(val)
9 print(__val)
NameError: name 'val' is not defined
静态方法可以通过实例来调用,也可以通过类来调用,但两种调用都不可以访问类变量,不管是私有变量还是非私有变量。