你真的了解Python中的类class?

简介: 在Python的类中,有着类属性、实例属性,静态方法、类方法、实例方法的区别。到底有什么不一样呢?接下来我们就一探究竟。

概述

在Python的类中,有着类属性、实例属性,静态方法、类方法、实例方法的区别。到底有什么不一样呢?接下来我们就一探究竟。

<br/>

类属性、实例属性

来看下简单的 Student 类的例子


class Student(object):

    # 类属性
    school = '井冈山大学'

    def __init__(self, name):

        # 实例属性
        self.name = name

其中 schoolStudent 类的类属性,name 则是实例属性。

ipython 中测试一下如何访问其属性

In [5]: stu1 = Student('hui')

In [6]: stu2 = Student('wang')

In [7]: stu3 = Student('zack')

In [8]: stu1.name, Student.school
Out[8]: ('hui', '井冈山大学')

In [9]: stu2.name, Student.school
Out[9]: ('wang', '井冈山大学')

In [10]: stu3.name, Student.school
Out[10]: ('zack', '井冈山大学')

# 看看实例对象能不能访问类属性,类对象能不能访问实例属性
In [11]: stu1.name, stu1.school
Out[11]: ('hui', '井冈山大学')

In [12]: Student.name, stu1.school
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-12-b897e001b174> in <module>
----> 1 Student.name, stu1.school

AttributeError: type object 'Student' has no attribute 'name'

经过测试可以发现 实例属性需要通过实例对象来访问,类属性通过类来访问,但在测验中 stu1.school 实例对象也能访问类属性,为什么呢?

其实,实例对象也是间接的通过类对象进行访问的,在每一个实例对象中都有一个 __class__ 的属性,其指向的就是创建实例对象的类对象。stu1.__class__ 的指向就是 Student类对象。然后实例对象访问属性的规则是先访问实例属性,然后再根据实例对象的 __class__ 来访问类属性。如果都没有找到则报错。

In [15]: dir(stu1)
Out[15]:
['__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 
 ....
 
 'name',
 'school']

In [16]: stu1.__class__
Out[16]: __main__.Student

In [17]: stu1.__class__.school
Out[17]: '井冈山大学'

In [18]: id(Student)
Out[18]: 2011692023944

In [19]: id(stu1.__class__)
Out[19]: 2011692023944

可以看出 Studentstu1.__class__id() 都一样,说明其内存地址都一样。因此实例属性可以通过 __class__ 访问类属性。

存储方式如下图

类对象派生实例对象

<br/>

由上图可以看出:

  • 类属性在内存中只保存一份
  • 实例属性在每个对象中都要保存一份

<br/>

还是以上面的例子在 ipython 中对类属性的修改进行测验

In [24]: class Student(object):
    ...:
    ...:     # 类属性
    ...:     school = '井冈山大学'
    ...:
    ...:     def __init__(self, name):
    ...:
    ...:         # 实例属性
    ...:         self.name = name
    ...:

In [25]: stu1 = Student('hui')

In [26]: stu2 = Student('jack')

In [27]: stu1.name, stu1.school
Out[27]: ('hui', '井冈山大学')

In [28]: stu2.name, stu2.school
Out[28]: ('jack', '井冈山大学')

# 通过类对象进行修改
In [29]: Student.school = '清华大学'

In [30]: stu2.name, stu2.school
Out[30]: ('jack', '清华大学')

In [31]: stu1.name, stu1.school
Out[31]: ('hui', '清华大学')

# 通过实例对象进行修改
IIn [33]: stu1.school = '北京大学'

In [34]: stu1.name, stu1.school
Out[34]: ('hui', '北京大学')

In [35]: stu2.name, stu2.school
Out[35]: ('jack', '清华大学')

In [36]: Student.school
Out[36]: '清华大学'

In [37]: stu1.__class__.school
Out[37]: '清华大学'
    
In [39]: id(stu2.school)
Out[39]: 2011720409808

In [40]: id(Student.school)
Out[40]: 2011720409808

In [41]: id(stu1.school)
Out[41]: 2011720494992

# 通过实例对象的__class__属性修改
IIn [42]: stu2.__class__.school = '井冈山大学'

In [43]: stu1.name, stu1.school
Out[43]: ('hui', '北京大学')

In [44]: stu2.name, stu2.school
Out[44]: ('jack', '井冈山大学')

In [45]: Student.school
Out[45]: '井冈山大学'

<br/>

说明: 实例对象.类属性 = xxx 并没有修改到其类属性,而是在实例对象中创建了一个与类属性同名的实例属性。因此修改类属性,应该使用类对象进行修改。再外界最好不要使用 实例对象.新属性 = xxx,动态创建实例属性。

<br/>

使用场景

到底是用类属性,还是实例属性?

如果每个实例对象需要具有相同值的属性,那么就使用类属性,用一份既可。

class Province(object):
    # 类属性
    country = '中国'

    def __init__(self, name):
        # 实例属性
        self.name = name
        
p1 = Province('江西省')
p2 = Province('四川省')
        

<br/>

实例方法、静态方法和类方法

类中方法包括:实例方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同

  • 实例方法:由对象调用,至少一个 self 参数;执行实例方法时,自动将调用该方法的对象赋值给 self
  • 类方法:由类调用,至少一个 cls 参数;执行类方法时,自动将调用该方法的类赋值给 cls
  • 静态方法:由类调用,无默认参数。

<br/>


class Foo(object):

    foo = 'Foo'

    def __init__(self, name):
        self.name = name

    def instance_func(self):
        print(self.name)
        print(self.foo)
        print('实例方法')

    @classmethod
    def class_func1(cls):
        print(cls.foo)
        print('类方法1')

    @classmethod
    def class_func2(cls):
        print(cls.name)
        print('类方法二')

    @staticmethod
    def static_func():
        print('静态方法')

<br/>

其中 @classmethod 是装饰器,说明这是类方法,@staticmethod 则说明是静态方法。关于装饰器的内容这里就不在赘述了。

ipython 中测验一下各方法

# 实例对象调用
In [71]: f = Foo('hui')

In [72]: f.instance_func()
hui
Foo
实例方法

In [73]: f.class_func1()
Foo
类方法1

In [74]: f.class_func2()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-74-7d161e9e60ec> in <module>
----> 1 f.class_func2()

<ipython-input-60-7fc48649a96a> in class_func2(cls)
     18     @classmethod
     19     def class_func2(cls):
---> 20         print(cls.name)
     21         print('类方法二')
     22

AttributeError: type object 'Foo' has no attribute 'name'

In [75]: f.static_func()
静态方法

# 类对象自身调用
In [76]: Foo.instance_func()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-76-883efcb56130> in <module>
----> 1 Foo.instance_func()

TypeError: instance_func() missing 1 required positional argument: 'self'

In [77]: Foo.class_func1()
Foo
类方法1

In [78]: Foo.static_func()
静态方法

可以发现实例对象三种方法都可以调用,但 cls 类对象不能访问实例属性。类对象不能直接调用实例方法,类、静态方法可以。

<br/>

self与cls的区别

  • self 指的是类实例对象本身(注意:不是类本身)。
  • cls 指的是类对象本身
  • self 可以访问到类属性、实例属性,cls 只能访问类属性。

其中 self, cls 只是代指实例对象和类对象,因此换成其他变量也可以,但是约定成俗(为了和其他编程语言统一,减少理解难度),不要搞另类,大家会不明白的。

<br/>

使用场景

需要操作类属性的定义成类方法。

需要操作实例属性的定义成实例方法。

既不需要操作类属性,也不需要操作实例属性就定义成静态方法。

<br/>

公众号

新建文件夹X

大自然用数百亿年创造出我们现实世界,而程序员用几百年创造出一个完全不同的虚拟世界。我们用键盘敲出一砖一瓦,用大脑构建一切。人们把1000视为权威,我们反其道行之,捍卫1024的地位。我们不是键盘侠,我们只是平凡世界中不凡的缔造者 。
相关文章
|
1月前
|
供应链 数据挖掘 Serverless
【python】美妆类商品跨境电商数据分析(源码+课程论文+数据集)【独一无二】
【python】美妆类商品跨境电商数据分析(源码+课程论文+数据集)【独一无二】
【python】美妆类商品跨境电商数据分析(源码+课程论文+数据集)【独一无二】
|
23天前
|
Python
12类常用的Python函数
12类常用的Python函数
|
20天前
|
存储 程序员 Python
Python类的定义_类和对象的关系_对象的内存模型
通过类的定义来创建对象,我们可以应用面向对象编程(OOP)的原则,例如封装、继承和多态,这些原则帮助程序员构建可复用的代码和模块化的系统。Python语言支持这样的OOP特性,使其成为强大而灵活的编程语言,适用于各种软件开发项目。
15 1
|
23天前
|
存储 程序员 C++
python类及其方法
python类及其方法
|
22天前
|
C++ Python
python类方法中使用:修饰符@staticmethod和@classmethod的作用与区别,还有装饰器@property的使用
python类方法中使用:修饰符@staticmethod和@classmethod的作用与区别,还有装饰器@property的使用
12 1
|
1月前
|
对象存储 Python
Python代码解读-理解-定义一个User类的基本写法
以上描述清晰地阐述了如何在Python中定义 `User`类的基本方法以及如何创建和使用该类的实例。这是面向对象编程中的核心概念,是紧密结合抽象和实现,封装数据并提供操作数据的接口。由于用简单通用的语言易于理解,这样的解释对于初学者而言应该是友好且有帮助的。
22 4
|
1月前
|
程序员 Ruby Python
Python里的类和对象是什么?
本文介绍了Python中面向对象编程的核心概念——类与对象。类作为一种“蓝图”,定义了一组属性和方法,用于描述一类对象的共同特征与行为。通过类可以创建具体的对象实例,每个对象拥有类所定义的属性和方法。文章通过`Human`类的例子详细展示了如何定义类、初始化对象及其属性、定义方法,以及如何给对象添加自定义属性。此外,还介绍了如何通过类创建多个具有不同特性的对象实例,并探讨了属性覆盖和使用`@property`装饰器实现只读属性的方法。
|
27天前
|
测试技术 Python
Python 类中__init__方法的作用
【8月更文挑战第24天】
30 0
|
1月前
|
Python
Python笔记9 类
本文是作者的Python复习笔记第九篇,深入探讨了Python中的类和面向对象编程。文中详细解释了如何创建类、实例化对象、定义和使用类方法,以及类的继承、重写方法和嵌套类的使用。此外,还讨论了类模块的导入和导出,包括处理类之间的依赖关系。通过示例代码,文章展示了类在Python编程中的应用和重要性。
23 0
|
1月前
|
Python
Python常用工具类
Python常用工具类
14 0