详解Python 类变量与实例变量的陷阱

yizhihongxing

Python中的类变量和实例变量是常见的面向对象编程的概念。类变量是定义在类中,并且被所有实例共享的变量。实例变量是定义在实例中,并且每个实例有它们自己的独立变量副本。

然而,在使用类变量和实例变量时,有一些陷阱需要注意,下面我们就来详细讲解这些问题以及如何正确使用类变量和实例变量。

类变量与实例变量的区别

类变量是所有实例共享的变量,关键字 class 定义的变量是类变量。实例变量是每个实例独立的变量,关键字 self、或者其他实例本身定义的变量即为实例变量。

下面我们来使用示例代码演示这个区别:

class Person:
    count = 0

    def __init__(self, name):
        self.name = name
        Person.count += 1

p1 = Person("Tom")
p2 = Person("Jerry")

print(p1.count)  # 输出 2
print(p2.count)  # 输出 2

在这个例子中,我们定义了一个 Person 类,并定义了一个类变量 count。每当一个 Person 实例被创建时,count 的值都会增加1。由于 count 是类变量,因此被所有 Person 实例共享。因此,我们可以看到 p1.countp2.count 的值都为 2。

现在,我们尝试创建一个实例变量:

class Person:
    def __init__(self, name):
        self.name = name
        self.count = 0

    def add(self):
        self.count += 1

p1 = Person("Tom")
p2 = Person("Jerry")

p1.add()
p1.add()

print(p1.count)  # 输出 2
print(p2.count)  # 输出 0

在这个例子中,我们定义了一个 Person 类,并在 __init__ 方法中定义了一个实例变量 count。我们还定义了一个 add 方法,该方法每次被调用时会增加 count 的值。

在这个例子中,p1 调用了两次 add() 方法,因此 p1.count 的值为 2。但是,由于实例变量 count 是每个实例独立的,因此 p2 的值为 0。

陷阱及解决方法

下面我们来介绍一些使用类变量和实例变量时可能发生的陷阱以及如何避免它们。

陷阱1:类变量被多个实例修改

由于类变量是所有实例共享的,当多个实例同时修改类变量时可能会导致错误的结果。

例如,我们假设 Person 类的每个实例都有一个年龄,并且 Person 类中有一个 default_age 的类变量,用于指定所有实例的默认年龄:

class Person:
    default_age = 20

    def __init__(self, name, age=None):
        self.name = name
        if age is None:
            self.age = Person.default_age
        else:
            self.age = age

在这个例子中,如果一个 Person 实例没有指定年龄,它将使用 default_age 值。然而,这个方案有一个缺点,当 Person.default_age 的值改变时,所有使用默认值的实例年龄也会改变:

p1 = Person("Tom")
p2 = Person("Jerry")
print(p1.age)  # 输出 20
print(p2.age)  # 输出 20

Person.default_age = 30

p3 = Person("Lucy")
print(p1.age)  # 输出 30!
print(p2.age)  # 输出 30!
print(p3.age)  # 输出 30

解决方法:

为了避免这种问题,我们可以遵循以下两种方法之一:

  1. 使用实例变量而不是类变量来保存默认值:
class Person:
    def __init__(self, name, age=None):
        self.name = name
        self.default_age = 20
        if age is None:
            self.age = self.default_age
        else:
            self.age = age

p1 = Person("Tom")
p2 = Person("Jerry")
print(p1.age)  # 输出 20
print(p2.age)  # 输出 20

p3 = Person("Lucy", age=30)
print(p3.age)  # 输出 30

在这种情况下,每个实例都有自己的默认年龄,因此它们不会相互干扰。

  1. default_age 定义为不可更改的对象,例如元组:
class Person:
    default_age = (20,)

    def __init__(self, name, age=None):
        self.name = name
        if age is None:
            self.age = Person.default_age[0]
        else:
            self.age = age

p1 = Person("Tom")
p2 = Person("Jerry")
print(p1.age)  # 输出 20
print(p2.age)  # 输出 20

Person.default_age = (30,)

p3 = Person("Lucy")
print(p1.age)  # 输出 20
print(p2.age)  # 输出 20
print(p3.age)  # 输出 30

在这个例子中,我们将 default_age 定义为只有一个元素的元组。由于元组是不可更改的,因此当我们尝试修改 Person.default_age 时,会引发一个 TypeError。

陷阱2:类变量和实例变量同名

如果一个实例变量和一个类变量同名,那么在访问这个变量时会发生什么呢?

例如,考虑以下代码:

class Person:
    count = 0

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

p1 = Person("Tom")
print(p1.count)  # 输出 10
print(Person.count)  # 输出 0

在这个例子中,我们定义了一个 Person 类,并定义了一个类变量 count。我们还定义了一个实例变量 count,并在 __init__ 方法中将其值设置为 10。当我们打印 p1.count 时,输出的结果为 10。但是,当我们打印 Person.count 时,输出结果为 0。

这是因为在实例方法中,Python首先查找实例变量。如果找不到,它将继续查找类变量。因此,在这个例子中,当我们使用 p1.count 时,Python首先找到了实例变量,因此返回的是 self.count 的值,即 10. 而在访问 Person.count 时,Python找到了类变量,因此返回的是类变量的默认值,即 0.

解决方法:

为了避免这种问题,我们应该尽可能使用不同的变量名称。如果变量名称一定要相同,那么我们可以使用类名来引用类变量:

class Person:
    count = 0

    def __init__(self, name):
        self.name = name
        Person.count = 10

p1 = Person("Tom")
print(p1.count)  # 输出 10
print(Person.count)  # 输出 10

在这个例子中,我们在 __init__ 方法中使用了 Person.count 来设置类变量的值,以避免与实例变量发生冲突。

这就是关于Python中类变量与实例变量的陷阱及解决方法的详细讲解。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解Python 类变量与实例变量的陷阱 - Python技术站

(0)
上一篇 2023年3月25日
下一篇 2023年3月25日

相关文章

  • python使用reportlab画图示例(含中文汉字)

    下面给出“python使用reportlab画图示例(含中文汉字)”的完整攻略,包含以下内容: 标题:python使用reportlab画图示例(含中文汉字) 在使用Python进行数据分析的过程中,我们经常需要绘制出各种形式的图表来帮助我们更清晰地展示数据分析结果。reportlab是一个强大的Python报告工具包,它提供了多种图表绘制功能和中文支持。下…

    python 2023年5月18日
    00
  • windows7 32、64位下python爬虫框架scrapy环境的搭建方法

    一、安装Python3.6(64-bit) 1.到Python官网下载Python3.6的64-bit版本,下载地址为 https://www.python.org/downloads/release/python-360/ 2.安装Python3.6,安装过程中注意勾选“Add Python 3.6 to PATH”选项 3.打开命令提示符(cmd)输入“…

    python 2023年5月14日
    00
  • python数据XPath使用案例详解

    Python数据XPath使用案例详解 什么是XPath XPath是一种在XML文档中选择节点的语言,它也可以用来在HTML文档中进行选择。 在Python中,我们可以使用XPath来获取HTML文档中的节点信息,然后使用这些信息进行数据分析和挖掘。 XPath由路径表达式组成,它以/分隔的路径表示不同层次的节点,具有极高的灵活性。 如何使用XPath 安…

    python 2023年6月3日
    00
  • Python Tkinter Canvas画布控件详解

    Python Tkinter Canvas是一个非常强大的绘图工具,能够让用户轻松创建图形界面应用程序。下面我们来详细探讨一下Tkinter Canvas画布控件的详细使用方法。 Canvas的基本使用 Canvas是用于绘制图形的画布控件,在程序中引用如下: from tkinter import * root = Tk() canvas = Canvas…

    python 2023年6月13日
    00
  • Django动态展示Pyecharts图表数据的几种方法

    那我就来详细讲解一下“Django动态展示Pyecharts图表数据的几种方法”的完整攻略吧。 1. 背景介绍 Django是一款常用的Python Web框架,Pyecharts是Python中一款非常好用的数据可视化库,如何在Django中利用Pyecharts展示图表数据,成为了一道需要解决的问题。 2. 方法一:直接将Pyecharts的html代码…

    python 2023年6月6日
    00
  • 正则表达式笔记三则

    以下是详细讲解“正则表达式笔记三则”的完整攻略,包括正则表达式的介绍、Python中re模块的使用、示例说明和注意事项。 正则表达式的介绍 正则表达式是一种用于匹配字符串工具,它可以用来检查一个字符串是否符合某种模式。正则表达式通常由一些特殊字符和普通字符组成,用于描述字符串的特征。 Python中re模块的使用 在Python中可以使用re模块来处理正则表…

    python 2023年5月14日
    00
  • Python3基础之函数用法

    Python3基础之函数用法攻略 在Python中,函数是一段封装了特定功能的代码片段。当需要反复执行相同的内容时,我们可以将这段代码封装成一个函数。函数的定义以def关键字为开头,后面跟上函数名、形参、以及函数体。在调用时,只需要使用函数名和实参即可。 函数定义 函数的定义通常包括以下几个部分: 函数名 函数名是用来调用函数的,它要求是唯一的、有意义的,以…

    python 2023年6月5日
    00
  • Python 2.7中文显示与处理方法

    Python 2.7是一个老版本的Python,但在一些项目中还是需要使用它。而对于中文数据的处理和显示,可能会遇到一些问题。下面是Python 2.7中文显示与处理方法的攻略: 1. 字符编码的处理 1.1 在Python 2.7中,默认字符串编码是ASCII,如果要处理中文,需要使用Unicode编码。 # 讲中文字符串转换为Unicode编码 chin…

    python 2023年5月20日
    00
合作推广
合作推广
分享本页
返回顶部