python面向对象基础理论

python学习笔记之面向对象理论基础

面向对象基础理论

基本哲学

  1. 世界是由对象组成的

  2. 对象具有运动规律和内部状态

  3. 对象之间的相互作用和通讯构成世界

对象的特性

  1. 唯一性:世界上没有完相同的树叶,对象都有唯一块内存地址

  2. 分类性:分类是对现实世界的抽象

OOP的三大特征

  1. 继承

  2. 多态,相同的操作在不同的对象上得到的结果是不一样的

  3. 封装

面向对象的本质

  1. 面向对象是对数据和行为的封装

  2. 但是有时候,数据仅仅是数据,方法仅仅是方法

数据和行为的封装

以“门”这个对象来举例,门有门牌号,有开着的状态,有关闭的状态,让可以有打开的行为,也有关闭的行为。

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:
pass

等价于:

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,类中只有一个构造方法,接下实例化此类:

1
a = A(5)
1
a.x
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
1
b = B(5)
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)
1
c = C(5)
1
c.get_val()
5
1
c.__val
---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-24-edab75c4725a> in <module>()
----> 1 c.__val


AttributeError: 'C' object has no attribute '__val'
1
c.__add(2)
---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-21-ca5cc7410971> in <module>()
----> 1 c.__add(2)


AttributeError: 'C' object has no attribute '__add'
1
c.inc(2)
1
c.get_val()
7

C这个类实例化后,运行c.get_val()能正确得到变量__val的值,但直接用c.__val访问实例变量抛出了AttributeError错误,这是因为在构造函数中的__val这个实例变量名以双下划线开始且不以双下划线结束,这样命名的变量叫私有变量,此变量只能在类中调用,在类外不可访问。在调用c.__add(2)时抛出错误同样是因为这是一个私有方法,只在类内部调用,调用c.inc(2)就是很好的证明。

事实上在python中并没有真正实现对象的私有化,只是把对象的名称进行改名而已,如下:

1
dir(c)   #查看实例的属性信息
['_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']
1
c._C__val   #这样就可以访问到类的私有属性
7

本来访问类变量是c.__val,python为了让私有变量不让外部访问,把这个私有变量重新命名为“_类名__变量名”,在实际编程一般不会这样来访问一个私有对象。

类变量

类变量就是定义在类中而在类方法之外的变量,以一个例子来说明:

1
2
3
4
class D:
val = 5
def __init__(self):
pass

上边的val就是一个类变量,接下来实例化此类:

1
d1 = D()
1
d2 = D()
1
d1.val
5
1
d2.val
5
1
d1.val += 2
1
d1.val
7
1
d2.val
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
f1 = F()
1
f2 = F()
1
f1.val
[1, 2, 3]
1
f2.val
[1, 2, 3]
1
f1.val.append(4)
1
f1.val
[1, 2, 3, 4]
1
f2.val
[1, 2, 3, 4]

当执行f1.val.append(4)后,f1f2两个实例中的"val"变量都发生了改变,为什么在上边D这个类中只有一个被修改了?

这是因为在实例化后,类变量是各个实例共享的对象,都指向同一个内存地址,起先d1.va1d2.val是指向同一个内存地址,f1.valf2.val是指向同一内存地址,但d1.va1d2.val指向的是一个数字,而f1.valf2.val指向的是一个列表,数字是一个不可变对象,列表是一个可变对象,所以在执行d1.val += 2后,d1.val新创建了一个指向数字“7”的对象,而d2.val还是指向实例化时指向数字“5”的对象;而向列表中增加一个元素并不会创建一个新的对象,所以f1.valf2.val依然指向同一个内存地址。

另外,类变量可直接用“类名.变量名”的方式获取,如下:

1
F.val
[1, 2, 3, 4]

再来看一个例子,如下:

1
2
3
4
5
class G:
__val = 3

def get_val(self):
return self.__val
1
G.__val
---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-51-8de1b6f555b8> in <module>()
----> 1 G.__val


AttributeError: type object 'G' has no attribute '__val'
1
g1 = G()
1
g1.__val
---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-54-376589fc2b09> in <module>()
----> 1 g1.__val


AttributeError: 'G' object has no attribute '__val'
1
g1.get_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类本身。

1
h = H(7)
1
H.get_val()
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'
1
h.get_val()
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)
1
i = I()
1
i.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
1
I.print_val()
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

静态方法可以通过实例来调用,也可以通过类来调用,但两种调用都不可以访问类变量,不管是私有变量还是非私有变量。

文章目录
  1. 1. 面向对象基础理论
    1. 1.1. 基本哲学
    2. 1.2. 对象的特性
    3. 1.3. OOP的三大特征
    4. 1.4. 面向对象的本质
    5. 1.5. 数据和行为的封装
    6. 1.6. 构造方法
    7. 1.7. 访问控制
    8. 1.8. 类变量
    9. 1.9. 类方法
    10. 1.10. 静态方法
|