Python 进阶_OOP 面向对象编程_实例属性和方法

目录

  • 目录
  • 构造器和解构器
    • 构造器 __init__
    • 真构造器 __new__
    • 解构器 __del__
  • 实例方法
    • Python 中的 抽象方法
  • 实例属性
    • 查看实例属性
  • 实例属性和类属性的区别
    • 访问不可变类属性
    • 访问可变类属性

构造器和解构器

构造器 __init__()

类函数 __init__() 是 Python 类中预定义的方法,需要被重载才会生效。以双下划线 “__” 开头和结尾, 在 Python 中使用这种命名方式的方法会被理解为是一种特殊方法, Python 的特殊方法功能非常丰富, 种类也很多, 在声明变量名的时候要注意不要和特殊方法重名.

通常,构造器用于在 实例化对象被创建后,返回这个实例之前 的这段时间里,执行一些特定的任务或设置。例如:初始化实例对象属性(以 self 关键字调用的属性)。它在实例化一个新的对象时被自动调用,所以除了初始化实例属性之外,还常被用于运行一些初步的诊断代码。其调用的具体步骤:
1. 创建类的实例化对象
2. Python 解析器检查类是否实现了构造器
3. 若有,则执行构造器的实现,且要求创建对象的时候传入对应的实参。实例对象会传递给第一个形参 self 。
4. 若没有,则直接返回实例对象

一般建议在构造器中设定需要初始化的实例属性,而且构造器应该返回 None,即没有 return 语句。

真·构造器 __new__()

__init__() 相比 __new__() 才是真正的构造器,实际上,在 Python 解析器中是先调用了 __new__() 生成一个实例,再将该实例对象传入 __init__() 实现初始化操作。但 __new__() 很少需要我们去重载,一般只有在派生了不可变类型的子类后需要重载,EG. 派生 String/Int/Tuple 等

为什么说 __new__() 是真·构造器呢?
因为这个特殊的类方法是真的返回了一个类的实例对象,而不像 __init__() 是传入了一个实例化对象。

EXAMPLE:不可表类型的派生

class RoundFloat(float):
    def __new__(cls, val):
        return float.__new__(cls, round(val, 2)) #因为 __new__ 是一个类方法,所以我们要显式的传递一个类对象

类 RoundFloat 是类 float 的子类,我们通过重载父类的 __new__() 构造器来定制一个新的不可变类型(Python 2.2之后将类和类型统一了,所以可以继承 Python 的内置数据类型)。当实例化 RoundFloat 的对象时,实际上是实例化了Python 内置数据类型 Float 的对象,并对这个对象做了一些定制化的操作(round(val, 2))。
NOTE:即便我们也可以通过重载 __init__() 来实现这个结果,但这里却不能这么做。因为如果 __new__() 没有被重载的话,仍会默认调用父类 Float 的构造器,创建 Float 类型的对象,而不是创建现在的 RoundFloat 类型对象。这也是两者的本质区别。

解构器 __del__()

解构器 __del__() 会在实例对象被垃圾回收之前被 Python 解析器调用一次(只会调用一次,之后就回收实例对象)。解构器是实例对象释放资源前提供特殊处理功能的方法。一般我们很少会用到,但解构器是检查对象引用状态的好方法,加入对象仍然存在引用时,解构器是不会执行的。

注意不要随便重载解构器。

实例方法

实例方法的基本特征就是要传入一个实例对象作为实参。
在 Python 看来,实例方法严格来说属于类属性的一种,实例方法只能通过实例化对象来调用,无法通过类来直接调用。

In [59]: class AClass(object):
    ...:     LOG = 'Define a class'
    ...:     def my_no_actioon_method(self):
    ...:         pass
    ...:

In [60]: dir(AClass)
Out[60]:
['LOG',
 '__class__',
 '__delattr__',
 '__dict__',
 '__doc__',
 '__format__',
 '__getattribute__',
 '__hash__',
 '__init__',
 '__module__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 'my_no_actioon_method']     # 通过 dir() 可以找到实例方法存在于类的属性列表中。

In [62]: AClass.my_no_actioon_method()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-62-33bc36ae78f7> in <module>()
----> 1 AClass.my_no_actioon_method()

TypeError: unbound method my_no_actioon_method() must be called with AClass instance as first argument (got nothing instead)

如果类直接调用实例方法的话,会触发 TypeError,因为实例方法需要绑定一个实例化对象,这就是 self 存在的意义。所有的含有 self 关键字的属性和方法都只能通过类的实例化对象来调用。所以,一般而言类中定义的实例方法都需要含有 self 形参,当然也可以通过别的方式来解除这种约束,这个我们后面会说到。

In [63]: a_object = AClass()

In [64]: a_object.my_no_actioon_method() #类方法是通过局点标识符来与它的实例化对象绑定的

Python 中的 “抽象方法”

Python 并不支持 Java 中的抽象方法,但可以通过在父类中 ``raise NotImplememtedError 来实现同样的效果。抽象方法的意义在于可以强制的规范在子类中必需定义与父类中方法同名的子类成员方法。

In [74]: class AClass(object):
    ...:     LOG = 'Define a class'
    ...:     def my_method(self):
    ...:         raise NotImplementedError()
    ...:
    ...:

In [75]: class BClass(AClass):
    ...:     pass
    ...:

In [76]: b_object = BClass()

In [77]: dir(b_object)
Out[77]:
['LOG',
 '__class__',
 '__delattr__',
 '__dict__',
 '__doc__',
 '__format__',
 '__getattribute__',
 '__hash__',
 '__init__',
 '__module__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 'my_method']

In [78]: b_object.my_method()
---------------------------------------------------------------------------
NotImplementedError                       Traceback (most recent call last)
<ipython-input-78-49e04830d861> in <module>()
----> 1 b_object.my_method()

<ipython-input-74-d6213dfecb1b> in my_method(self)
      2     LOG = 'Define a class'
      3     def my_method(self):
----> 4         raise NotImplementedError()
      5
      6

NotImplementedError:

所以通过这种方式,程序员们只能乖乖在子类的重载这个同名方法了,这对程序的规范做了很好的约束。

In [79]: class BClass(AClass):
    ...:     def my_method(self):
    ...:         print "overload the function."
    ...:
    ...:

In [80]: b_object = BClass()

In [81]: b_object.my_method()
overload the function.

实例属性

在类定义中,通过 self 关键字来调用的属性,即属于实例对象的属性,类对象是不能调用的。类属性的创建实际上是通过调用实例方法 __setattr__ 来完成的。x.__setattr__('name', value) <==> x.name = value,除此之外还有内建函数 setattr(),setattr(object, name, value) <==> object.name = value

Python 可以在任意的时间内创建一个命名空间,所以我们可以在创建了实例对象之后定义实例属性,也可以在定义类的时候创建实例属性。后者创建实例属性最重要的方式就是构造器 __init__()

In [98]: class AClass(object):
    ...:     def __init__(self, name, age):
    ...:         self.name = name
    ...:         self.age = age
    ...:

In [99]: a_object = AClass('JMILKFAN', 24)

In [101]: a_object.name
Out[101]: 'JMILKFAN'

In [102]: a_object.age
Out[102]: 24

In [103]: a_object.sex = 'man'

In [104]:  a_object.sex
Out[104]: 'man'

当然,我们除了可以在构造器中创建实例属性之外,还可以在类体的任意地方创建,当我们并不推荐这么做。

查看实例属性

查看实例属性和查看类属性是一样的,可以通过 dir()instance.__dict__ 来查看。
EXAMPLE

In [105]: dir(a_object)
Out[105]:
['__class__',
 '__delattr__',
 '__dict__',
 '__doc__',
 '__format__',
 '__getattribute__',
 '__hash__',
 '__init__',
 '__module__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 'age',
 'name',
 'sex']

In [106]: a_object.__dict__
Out[106]: {'age': 24, 'name': 'JMILKFAN', 'sex': 'man'}

再与查看类属性做一个对比:

In [108]: dir(AClass)
Out[108]:
['__class__',
 '__delattr__',
 '__dict__',
 '__doc__',
 '__format__',
 '__getattribute__',
 '__hash__',
 '__init__',
 '__module__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__']

In [109]: AClass.__dict__
Out[109]:
dict_proxy({'__dict__': <attribute '__dict__' of 'AClass' objects>,
            '__doc__': None,
            '__init__': <function __main__.__init__>,
            '__module__': '__main__',
            '__weakref__': <attribute '__weakref__' of 'AClass' objects>})

NOTE: 对于类或实力对象来说 __dict__ 都是可以手动修改的,但是并不建议我们手动去修改这个属性字典。

我们还能够通过内置方法 getattr() 或实例方法 __getattribute__() 来查看

In [119]: getattr(a_object, 'name')
Out[119]: 'JMILKFAN'

In [122]: a_object.__getattribute__('name')
Out[122]: 'JMILKFAN'

实例属性和类属性的区别

类属性与实例无关,类属性的值不会因为实例化对象而改变,除非在实例对象中 显式 的改变了 可变类属性 的值。类属型的值就像是静态成员那样被使用,我们一般采用 class.attr 的方式来调用类属性 attr,除此我们也能够使用实例对象来调用类属性 instance.attr,但前提是实例对象中没有同名的实例属性,否则 instance.attr 中的 attr 就是一个实例属性,返回实例属性的值。相反,实例属性只能由类实例对象来调用。

类属性类似于一个静态属性,在定义类属性的时候会在内存开辟静态空间,用于存放。而且这个静态空间是类的所有实例对象都可以访问的,所以类属性是被所有的由该类实例化的对象共享的。

In [150]: class AClass(object):
     ...:     version = 1.0
     ...:

In [151]: a_object = AClass()

In [152]: a_object.version
Out[152]: 1.0

In [153]: AClass.version += 1

In [154]: a_object.version
Out[154]: 2.0

类属性的改变会被实例对象感知。

访问不可变类属性

对于不可变类属性(int/str/tuple)而言,实例对象是无法改变类属性的值得。但如果类属性是可变数据类型,那么实例对象就可以显式的修改其的值。

In [124]: class AClass(object):
     ...:     version = 1.0
     ...:

In [125]: a_object = AClass()

In [126]: AClass.version
Out[126]: 1.0

In [127]: a_object.version
Out[127]: 1.0

In [128]: AClass.version = 1.1

In [129]: a_object.version
Out[129]: 1.1

In [130]: AClass.version
Out[130]: 1.1

In [131]: a_object.version
Out[131]: 1.1

In [132]: b_object = AClass()

In [133]: b_object.version
Out[133]: 1.1

In [134]: b_object.version = 1.2   # 这里并不没有修改类属性,而是创建了一个新的实例属性

In [135]: AClass.version
Out[135]: 1.1

In [136]: a_object.version
Out[136]: 1.1

从上面的例子可以看出,实例对象可以引用类属性(前提是实例对象没有同名的实例属性),却不能改变类属性,而是创建了一个新的实例属性并覆盖了同名的类属性。

访问可变类属性

In [143]: class AClass(object):
     ...:     aDict = {'name': 'jmilkfan'}
     ...:

In [144]: a_object = AClass()

In [146]: AClass.aDict
Out[146]: {'name': 'jmilkfan'}

In [147]: a_object.aDict
Out[147]: {'name': 'jmilkfan'}

In [148]: a_object.aDict['name'] = 'fanguiju'

In [149]: AClass.aDict
Out[149]: {'name': 'fanguiju'}

实例对象可以修改类属性的本质是,实例对象在没有创建一个新的实例属性的基础上进行了修改。

注意:一般认为通过实例对象来修改类属性是危险的,会有很多潜在的风险。所以建议使用类名来修改类属性,而不是实例对象名。

时间: 2024-08-23 20:17:28

Python 进阶_OOP 面向对象编程_实例属性和方法的相关文章

Python 进阶_OOP 面向对象编程_类属性和方法

目录 目录 类属性 调用类属性 查看类属性 特殊的类属性 类方法 真构造器 __new__ 类属性 在理解类属性之前要先搞清楚 实例属性 和 函数属性 之间的区别: 1. 实例属性:指的是实例化类对象的属性,需要在类中使用 self 关键字来将其和实例化对象绑定的属性. 2. 函数属性:指的是定义在函数体内的属性,其可以是实例属性,也可以是类属性. 3. 类属性:是一个与实例无关的属性,比起实例属性而言,它更加的 静态,当定义在类方法中时,并不会因为方法调用的完毕而被回收.类属性,在类定义中直接

Python 进阶_OOP 面向对象编程_组合与继承

#目录 前言 组合 派生 通过继承来覆盖重载方法 最常用的重载场景实例方法的重载 从标准类中派生类方法的重载 前言 我们定义一个类是希望能够把类当成模块来使用,并把类嵌入到我们的应用代码中,与其他的数据类型.逻辑执行流结合使用.一般来说我们可以使用两种方法在代码中利用类,那就是组合和派生. 组合 组合: 就是将不同的类混合并加入到其他类中,来 增加类的功能 / 提高代码的重用性 / 易于维护(对类的修改会直接反应到整个应用中) .我们可以实例化一个更大的对象,同时还可以添加一些实例属性和实例方法

Python 进阶_OOP 面向对象编程_类和继承

目录 目录 类 最简单的类 类方法 构造器 __init__ 创建一个类 实例化一个对象 调用实例的方法和属性 创建子类 使用 super 来调用父类的构造器 实例化子类对象 调用子类的属性和方法 类属性方法的命名规则 类 类是实例的抽象, 实例是类的具体化. Python 中的类分为 经典类 和 新式类. 前者现在已经很少用到了, 其特点就是可以不需要继承任何父类:后者则刚好相反, 形式类必须至少有一个父类, 如果没有特定的父类需要继承时, 至少需要继承基类 object, 在后面给出例子.

Python 进阶_OOP 面向对象编程_静态方法和类方法

目录 目录 静态方法 类方法 使用函数修饰符来声明静态方法和类方法 静态方法 静态方法仅是类中的函数, 不需要绑定实例, 也就是说静态方法的定义不需要传入 self 参数. 静态方法不属于类的某一个实例对象, 而是属于类本身, 所以不需要绑定到实例对象. 可以通过 className.staticMethodName 的方式来调用. 特点: 只能使用静态变量, 所以始终占用同一个内存, 执行效率更高, 但不会被自动回收. 应用场景: 用于处理一些不会经常改变, 但却会被频繁调用的数据. EG:

Python 进阶_OOP 面向对象编程_self 的实例绑定

目录 目录 self 和绑定 调用非绑定的方法 self 和绑定 在 Python 中 self 变量是特殊的, 其用于在实例方法中引用该方法所绑定的实例, 换句话说就是 Python 在实例化对象时会自动的将实例对象传递到类体的 self 变量中, 类体中的 self 变量代表了该类的实例对象. 实例方法无论在任何地方被调用, 实例对象总是作为第一个参数被传递进去, 从而实现了实例对象和实例方法的绑定, 这也是为什么同一个类可以实例化多个不同的实例化对象的原因之一. 所以, 当我们定义类实例方

Python类及面向对象编程【转】

Python类及面向对象编程 类是用来创建数据结构和新类型对象的主要机制.本章的主题就是类,面向对象编程和设计不是本章的重点.本章假定你具有数据结构的背景知识及一定的面向对象的编程经验(其它面向对象的语言,比如java,c++).(参见第三章,类型和对象 了解对象这个术语及其内部实现的附加信息) WeiZhong补充: 这本书出版于2001年,虽然Python有极佳的向下兼容性,但我们应该学习最新的知识.本章很多地方已经明显过时,为了保证大家学到新的知识并维持这本书的完整性,我会在必要的地方说明

Javascript简单实现面向对象编程继承实例代码_javascript技巧

本文讲述了Javascript简单实现面向对象编程继承实例代码.分享给大家供大家参考,具体如下: 面向对象的语言必须具备四个基本特征: 1.封装能力(即允许将基本数据类型的变量或函数放到一个类里,形成类的成员或方法) 2.聚合能力(即允许类里面再包含类,这样可以应付足够复杂的设计) 3.支持继承(父类可以派生出子类,子类拥有父母的属性或方法) 4.支持多态(允许同样的方法名,根据方法签名[即函数的参数]不同,有各自独立的处理方法) 这四个基本属性,javascript都可以支持,所以javasc

php面向对象中static静态属性与方法的内存位置分析

 这篇文章主要介绍了php面向对象中static静态属性与方法的内存位置,通过内存位置实例分析了static静态属性的原理与使用技巧,需要的朋友可以参考下     本文实例分析了php面向对象中static静态属性与方法的内存位置.分享给大家供大家参考.具体如下: static静态属性的内存位置-->类,而不是对象.下面做测试来证明一下 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php header("content-type:tex

python打开文件并获取文件相关属性的方法_python

本文实例讲述了python打开文件并获取文件相关属性的方法.分享给大家供大家参考.具体分析如下: 下面的代码通过open函数打开文件,并输出文件名.打开状态.打开模式等属性 #!/usr/bin/python # Open a file fo = open("foo.txt", "wb") print "Name of the file: ", fo.name print "Closed or not : ", fo.clos