面向对象

Posted wgbo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面向对象相关的知识,希望对你有一定的参考价值。

对象的相关定义:

  对象:对象指的是类的实例。一个对象有自己的状态(属性)行为(方法)和唯一的标识(内存地址);所有相同类型的对象所具有的结构和行为在它们共同的类中被定义。

  状态:包括这个对象已有的属性(通常是类里面已经定义好的),再加上对象具有的当前属性值(往往是动态的)

  行为:指一个对象如何影响外界和被外界影响,表现为对象自身状态的改变和信息的传递。

  标识:值一个对象所具有的区别于所有其他对象的属性(本质上指内存中所创建的对象的地址)

简言之,对象应具有属性(即状态)、方法(即行为)和标识。标识是在内存中自动完成的,不用去管它,平时主要用到属性和方法。

  类:类描述了所创建的对象共同的属性和方法(数据属性和方法属性)

  实例:类是对象的定义,实例才是真实的物件,是具体的个体。实例只有数据属性,没有方法属性。实例调用的方法是类中的方法,由作用域决定。

 

属性:

  类属性:在类中定义的变量,即类的属性。

>>> class A:
...     x = 7    # 类中定义变量x
...
>>> A.x    # 通过类名调用类中变量x
7

  类的属性本质上就是类中的变量,它的值不依赖于任何实例,只是由类中所写的变量赋值语句确定。所以这个类属性又叫静态变量或静态数据。类属性只会在创建类的时候定义一次,不会因为对象的创建而重新创建。

  类属性可以增加、删除或者修改。

>>> A.y = 9    # 直接向类A中增加新的属性
>>> A.y
9
>>> del A.x    # 删除类中的已有属性
>>> A.x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object A has no attribute x
>>> A.y = 99    # 修改类中的属性值
>>> A.y
99

  查看类中的内容:

>>> dir(A)
[__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__, y]
>>> A.__doc__    # 显示类的文档
>>> A.__name__    # 以字符串的形式返回类的名字
A
>>> A.__base__    # 类的父类
<class object>
>>> A.__dict__    # 以字典的形式显示类的所有属性
mappingproxy({__doc__: None, __module__: __main__, __dict__: <attribute __dict__ of A objects>, __weakref__: <attribute __weakref__ of A objects>, y: 99})
>>> A.__module__    # 类所在的模块
__main__

  类属性的总结:

    类属性跟类绑定,可以自定义、删除、修改值,也可以随时增加类属性;

        每个类都有一些特殊属性。

  创建实例:创建实例,就是调用类。

    当类被调用之后:

    创建实例对象;

    检查是否实现__init__()方法。如果没有实现,返回实例对象;如果实现了__init__(),则调用该方法,并且将实例对象作为第一个参数self传递进去。

  __init__()是一个特殊方法,它一般规定一些属性或者初始化,让类具有一些基本的特征。__init__()方法没有return语句。

  实例属性:与类属性类似,实例所具有的属性叫做实例属性。每个实例的属性定义在该实例的命名空间(内存地址)中,通过实例可以在不同的方法里直接共享实例属性。不同于函数中的局部变量,只能在函数自己内部使用,不能在其他函数中直接调用。

 

>>> class A:
...     x = 7
...
>>> a = A()
>>> A.x    # 类属性
7
>>> a.x    # 实例属性
7
>>> a.x += 1    # 修改实例属性
>>> a.x
8
>>> A.x    # 类属性不变
7

  类属性和实例属性的区别在于,类属性是直接定义在类中的属性,而实例属性是通过实例化的对象去操作与类中同名的引用。相当于实例新建了一个新的属性,这个属性与类中的属性同名。所以修改实例属性不会影响类属性。

  类属性能够影响实例属性,这是因为实例就是通过调用类来建立的。即实例属性跟着类属性的改变而改变。以上都是类中变量引用不可变对象的结果,比如字符串和数。

  类中变量引用可变数据时,则情形会不同,因为可变数据能够进行原地修改。

>>> class B:
...     lst = [1, 2, 3]    # 类中属性是可变类型
...
>>> b = B()    # 类的实例
>>> B.lst
[1, 2, 3]
>>> b.lst
[1, 2, 3]
>>> b.lst.append(9)    # 修改实例的属性
>>> b.lst
[1, 2, 3, 9]
>>> B.lst    # 类属性也变化了
[1, 2, 3, 9]
>>> B.lst.append(python)    # 修改类属性
>>> B.lst
[1, 2, 3, 9, python]
>>> b.lst    # 实例属性也变化了
[1, 2, 3, 9, python]

  当类中变量引用的是可变对象时,类属性和实例属性都能直接修改这个对象,从而影响另一方的值。

  如果增加一个类属性,相应的也增加了一个实例属性。增加实例属性,类属性不会跟着增加。

>>> B.y = java    # 直接增加了一个类属性
>>> b.y    # 实例属性相应的增加
java
>>> b.z = 99    # 增加一个实例属性
>>> b.z
99
>>> B.z    # 类中没有此属性
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object B has no attribute z

  以上的实例属性或类属性,都是源自类中的变量所引用的值,是静态数据。还有一种实例属性的生存方法,是在实例创建的时候,通过__init__()初始化方法建立,是动态数据。

  self的作用:类中的任何方法,第一个参数都是self

  看下面的例子:

>>> class Stu:
...     def __init__(self):
...         self.name = Jack
...         self.age = 18
...         print(self)    # 打印self的内存地址
...         print(type(self))    # 打印self的类型
...
>>> stu = Stu()    # 实例化时自动执行类中__init__()方法
<__main__.Stu object at 0x7fc4dd792780>    # self的地址
<class __main__.Stu>    # self的类型
>>> print(stu)    # 与self对应的实例的地址
<__main__.Stu object at 0x7fc4dd792780>
>>> type(stu)     # 实例的类型
<class __main__.Stu>

  上面可以看到,self的内存地址和类型与实例stu完全一致。这说明self就是Stu类的实例,即selfstu引用了同一个实例对象。当创建实例的时候,实例变量作为第一个参数,被python解释器传给了self,所以初始化函数中的self.name就是初始化实例的属性。self和实例可以理解为一个主内、一个主外,实例对象的引用会被传给selfself就是实例在类的内部的形式。

  这样,在类的内部,通过self实例,类中所有方法都能承接self实例对象,它的属性也被带到类中的每个方法之中,实现了数据在类内部的流转。

 

以上是关于面向对象的主要内容,如果未能解决你的问题,请参考以下文章

面向面试编程代码片段之GC

PHP面向对象之选择工厂和更新工厂

Java中面向对象的三大特性之封装

python之路之前没搞明白4面向对象(封装)

Scala的面向对象与函数编程

Python面向对象学习之八,装饰器