精华内容
下载资源
问答
  • Python变量和数据类型

    2020-12-16 04:03:13
    本节内容如下:什么是变量Python中的数据类型变量的定义与赋值什么是变量变量是程序用来保存数据的内存单元,可以通过变量名称来操作这些数据和内存分配情况。既然,变量要占用内存空间,那么,就要考虑如何分配内存...

    本节内容如下:什么是变量

    Python中的数据类型

    变量的定义与赋值

    什么是变量

    变量是程序用来保存数据的内存单元,可以通过变量名称来操作这些数据和内存分配情况。既然,变量要占用内存空间,那么,就要考虑如何分配内存的问题,何时分配、分配多大、何时回收。不过这些在Python中都非常简单,Python通过自动内存管理以及实现了这些功能。

    Python中的数据类型查看原文

    上面我们说了变量是用来保存数据的内存空间,那么,这个空间该如何分配,分配多大呢?这些是又变量的数据类型决定的。每一种数据类型都有相对固定的大小。数据类型确定了,要分配的空间大小也就确定了。下面我们来看看Python中的数据类型都有哪些:

    1.数字

    一个整数或者一个小数。

    2.字符串

    单引号或者双引号扩起来的字符串,例如:'Hello' "Python"

    3.列表

    方括号括起来的一组数据,例如:[1,2,3,4]

    4.元组

    圆括号括起来的一组数据,例如:(404,'Page not found')

    5.字典

    大括号扩起来的键值对,例如: {name:'tom',age:20}

    6.对象

    类的实例,函数等。

    这些数据类型我们后面还会详细介绍,这里先简单了解一下。

    变量的定义与赋值

    在Python中定义变量和其他语言不太相同:

    1.不需要指定数据类型 2.定义变量时必须赋值 3.根据数据类型确定变量类型

    例如:

    a

    print(a) #错误 变量没有定义

    '''

    1.数字

    一个整数或者一个小数。

    '''

    age = 20

    price = 2.5

    print(type(age))

    print(type(price))

    age = '20'

    print(type(age))

    '''

    2.字符串

    单引号或者双引号扩起来的字符串,例如:'Hello' "Python"

    '''

    name = '享学课堂'

    site_name = "享学课堂"

    html = """

    """

    print(type(name))

    '''

    3.列表

    方括号括起来的一组数据,例如:[1,2,3,4]

    '''

    l = ['享学课堂','Python学院','老郭']

    print(type(l))

    for x in l:

    print(x)

    '''

    4.元组

    圆括号括起来的一组数据,例如:(404,'Page not found')

    '''

    t = (404,'页面未找到')

    print(type(t))

    '''

    5.字典

    方括号扩起来的键值对,例如: [name:'tom',age:20]

    '''

    d = {'name':'老郭', 'site':'2xkt.com','age':'20'}

    print(type(d))

    for k in d.keys():

    print(k)

    print(d[k])

    '''

    6.对象

    '''

    class Person(object):

    pass

    p = Person()

    print(type(p))

    展开全文
  • 变量是计算机内存中的一块区域,存储规定范围内的值,值可以被读取改变,通俗的说变量就是给数据起个名字,就像人的名字一样。 (1)变量命名规则 Python中常规的命名规则: 变量名由字母、数字、下划线组成,不能...
  • python变量数据保存

    千次阅读 2020-06-24 16:23:13
    文章目录字典数据dict1 使用yaml保存2 使用numpy保存 字典数据dict 1 使用yaml保存 参考:http://www.cocoachina.com/articles/92667 # yaml安装 conda install -c anaconda pyyaml 保存 import yaml data = { '...

    字典数据dict

    1 使用yaml保存

    参考:http://www.cocoachina.com/articles/92667

    # yaml安装
    conda install -c anaconda pyyaml         
    

    保存

    import yaml
    data = {
        'key1': 1,
        1: 2
    }
    with open('data.yml', 'w') as outfile:
        yaml.dump(data, outfile, default_flow_style=False)
    

    读取

    with open("data.yml", 'r') as stream:
        data = yaml.load(stream)
        print data[1] # 2
    

    2 使用numpy.save/load

    import numpy as np
    # Save
    dictionary = {'hello':'world'}
    np.save('my_file.npy', dictionary) 
    # Load
    read_dictionary = np.load('my_file.npy').item()
    print(read_dictionary['hello']) # displays "world"
    

    3 使用numpy.savetxt/loadtxt

    使用 np.savetxt 和 np.loadtxt 只能读写 1 维和 2 维的数组
    np.savetxt:将数组写入以某种分隔符隔开的文本文件中
    np.loadtxt:指定某种分隔符,将文本文件读入到数组中
    np.loadtxt()用于从文本加载数据。
    文本文件中的每一行必须含有相同的数据。

    loadtxt(fname, dtype=<class ‘float’>, comments=’#’, delimiter=None, converters=None, skiprows=0, usecols=None, unpack=False, ndmin=0)

    fname要读取的文件、文件名、或生成器。
    dtype数据类型,默认float。
    comments注释。
    delimiter分隔符,默认是空格。
    skiprows跳过前几行读取,默认是0,必须是int整型。
    usecols:要读取哪些列,0是第一列。例如,usecols = (1,4,5)将提取第2,第5和第6列。默认读取所有列。
    unpack如果为True,将分列读取。

    # pts= 数组点
    np.savetxt('filename.txt',pts,fmt="%.5f,%.5f,%.5f",delimiter='\n')
    #fmt="%.5f,%.5f,%.5f"是要以浮点说保存数据,小数点后保留5位小数。
    b = numpy.loadtxt("filename.txt", delimiter=',')
    

    4 保存为二进制文件

    使用数组的 tofile 函数可以方便地将数组中数据以二进制的格式写进文件

    a.tofile("filename.bin")
    b = np.fromfile("filename.bin",dtype = **) #读二进制文件,dtype需要指定,否则数据会读错
    

    该方法与np.save有几点区别:
    tofile函数只能将数组保存为二进制文件,文件后缀名没有固定要求。这种保存方法对数据读取有要求,np.fromfile 需要手动指定读出来的数据的的dtype,如果指定的格式与保存时的不一致,则读出来的就是错误的数据。

    tofile函数不能保存当前数据的行列信息,不管数组的排列顺序是C语言格式的还是Fortran语言格式,统一使用C语言格式输出。因此使用 np.fromfile 读出来的数据是一维数组,需要利用reshape指定行列信息。

    >>> a.shape = 3,4
    
    >>> a
    array([[ 0, 1, 2, 3],
        [ 4, 5, 6, 7],
        [ 8, 9, 10, 11]])
    >>> a.tofile("a.bin")
    >>> b = np.fromfile("a.bin", dtype=np.float) # 按照float类型读入数据
    >>> b # 读入的数据是错误的
    array([ 2.12199579e-314,  6.36598737e-314,  1.06099790e-313,
         1.48539705e-313,  1.90979621e-313,  2.33419537e-313])
    >>> a.dtype # 查看a的dtype
    dtype('int32')
    >>> b = np.fromfile("a.bin", dtype=np.int32) # 按照int32类型读入数据
    >>> b # 数据是一维的
    array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
    >>> b.shape = 3, 4 # 按照a的shape修改b的shape
    >>> b
    array([[ 0, 1, 2, 3],
        [ 4, 5, 6, 7],
        [ 8, 9, 10, 11]])
    

    参考

    1
    2

    展开全文
  •   我们知道,python的变量是有类型的,对于python变量的几种类型,我们在写python时是必须要有一定的概念的。知道类型就要知道变量类型怎么存储,可是为什么python的变量不需要声明类型就可以直接赋值?变量如果有...

      我们知道,python的变量是有类型的,对于python变量的几种数据类型,我们在写python时是必须要有一定的概念的。知道数据类型就要知道变量数据类型怎么存储,可是为什么python的变量不需要声明数据类型就可以直接赋值?变量如果有数据类型,那变量不是可以为任意数据类型?那真正的数据类型如int在内存存储的字节大小应该为多少?等等诸如一系列的问题让我提起了的兴趣,经过网上不断查找学习后,在此将我所了解到的内容在此做个总结归纳。


    一、变量的数据类型

    1、什么是变量的数据类型

      我们先捋一捋什么是变量,变量从字面上理解就是可以变化的量,我们可以随时改变这个变量的值,使得我们可以调用同一个变量而获得不同的值,与之对应的是常量。那么对于一个可变的变量,它有可能表示是一个字符串,一个数字或者是一个小数,因为这些在计算机内存里存放的方式是不一样的,所以简单理解就是变量的数据类型不同就是对应的数据在计算机内存中存放方式的不同。这种方式表现在按多少字节存储,是否连续存储等。
      我们都知道,c是静态类型语言,一种在编译期间就确定数据类型的语言,也就是我们需要对变量先声明其数据类型后才能使用,并且在使用过程中一般不能赋值一些超过该数据类型数值,比如:int a = 1.2,当然大类型是可以转向小类型的,如:double a = 1 (double类型接收整形数值)。可以肯定的,大多数静态类型语言都这么干。
      当然,python语言也有数据类型。但python语言不同,它是一种动态类型语言,又是强类型语言。它们确定一个变量的数据类型是在你第一次给它赋值的时候,也就是说你赋值给变量什么数据类型的数值,变量就是什么数据类型的。所以,对比之下,c语言变量的数据类型是事先定义的,而python是后天接受的

    2、python五大标准数据类型

      在讲变量存储之前,这里先简单总结下python的五大标准数据类型,为了方便展示,我们采用type方法显示变量的数据类型。

    (1)Numbers(数字)
      数字数据类型用于存储数值。他们是不可改变的数据类型,可简单分为以下四种:(注意这里十六进制,八进制都属于int整形。)
    int(整型):

    var = 520
    print(type(var))     	 # <class 'int'>
    

    float(浮点型):

    var = 5.20
    print(type(var))     	 # 输出:<class 'float'>
    

    bool(布尔型):

    var = true
    print(type(var))         # 输出:<class 'bool'>
    

    complex(复数):

    var = complex(13,14)
    print(type(var))   	     # 输出:<class 'complex'>
    

    (2)String(字符串)
      字符串或串是由数字、字母、下划线组成的一串字符,用‘’,“”,“‘ ’”都可表示。三者的使用可参考这篇文章: python字符串的各种表达方式.
      如下方代码所示,获得的类型为str类型。另外也顺便提一个小知识点,要访问字符串可以正向访问也可以反向访问,即正向时,var[0] = ‘p’,var[1] = ‘i’,var[2] = ‘g’;而反向时,var[-1] = ‘g’,var[-2] = ‘i’,var[-3] = ‘p’。

    var = “pig”
    print(type(var))   	     # 输出:<class 'str'>
    print(var[0:3])          # 正向访问,输出:'pig'
    print(var[-1])           # 反向访问,输出:'g'
    

    (3)List(列表)
      列表是 Python 中使用最频繁的数据类型,用 [ ] 标识。列表可以完成大多数集合类的数据结构实现。它可以同时包含字符,数字,字符串甚至可以包含列表(即嵌套)。如下方代码所示,列表的处理方式和字符串类似。

    var = [ 'pig' , 1 , 2.2 ]
    print(type(var))   	     # 输出:<class 'list'>
    print(var[0])   	     # 获得第一个元素,输出:'pig'
    print(var+var)  		 # 打印组合的列表,输出:[ 'pig', 1 , 2.2,'pig', 1 , 2.2 ]
    

    (4)Tuple(元组)
      元组类似于 List(列表)。元组用 () 标识。内部元素用逗号隔开。但是元组不能二次赋值,相当于只读列表。

    var = ( 'pig', 1 , 2.2 )
    print(type(var))   	     # 输出:<class 'tuple'>
    print(var[0])   	     # 获得第一个元素,输出:'pig'
    print(var+var)  		 # 打印组合的元组,输出:( 'pig', 1 , 2.2,'pig', 1 , 2.2 )
    var[0] = 'dog'			 # 出错!不能被二次赋值
    

    (5)Dictionary(字典)
      字典的相对于列表来说,列表是有序的对象集合,而字典是无序的对象集合。两者之间的区别在于字典当中的元素是通过键来存取的,而不是通过偏移存取。字典用"{ }"标识,字典由索引key和它对应的值value组成。

    dic = {'name''张三''age'18}
    print(dic ['name'])       # 得到键为'name' 的值,输出:'张三'
    print(dic [age])          # 得到键为'age' 的值,输出:18
    print(dic)                # 得到完整的字典,输出:{'name':'张三','age':18}
    print(dic.keys())         # 得到所有键,输出:dict_keys:(['name','age'])
    print(dic.values())       # 输出所有值,输出:dict_values:(['张三',18])
    

    二、python变量的存储

    1、变量与储存地址的关系

      在高级语言中,变量是对内存及其地址的抽象。以c语言举例, 变量事先定义好一种数据类型,于是编译器为变量分配一个对应类型的地址空间和大小(如int 4字节,char 1字节),当该变量改变值时,改变的只是这块地址空间中保存的值,即在程序运行中,变量的地址就不能再发生改变了。这种存储方式称为值语义。如下代码用VS2015运行,由结果可知,test变量的值被存储在0x0020FDC8,当变量改变时,地址不变,地址中对应的值发生改变。

    #include<iostream>
    using namespace std;
    int main()
    {
    	int test = 1;
    	cout << &test << ":" << test << endl;
    	test = 2;
    	cout << &test << ":" << test << endl;
    	return 0;
    }
    

    运行结果:

    0020FDC8:1
    0020FDC8:2
    

      这里就存在一个问题,每次新建一个变量,编译器就会开辟一块对应数据类型大小的内存,然后给那块内存取个名字(变量名)。除非一块内存被释放,那么该变量才能释放,不然一个变量就只能固定地对应一个数据类型。
      对此,python做出了改变,它采用了与高级语言截然不同的方式。在python中,一切变量都是对象,变量的存储采用了引用语义的方式,存储的只是一个变量的值所在的内存地址,而不是这个变量的值本身。简单理解就是,python变量只是某个数据的引用(可以理解成C语言的指针),当python变量赋值时,解释器(因为python为解释性语言)先为数值开辟一块空间,而变量则指向这块空间,当变量改变值时,改变的并不是这块空间中保存的值,而是改变了变量的指向,使变量指向另一个地址空间。这种存储方式称为对象语义或指针语义。举个例子:

    str = 'girls are pig'
    print(id(str))
    str = 'boys are dog'
    print(id(str))
    

    运行结果:

    113811696
    113812464
    

      id()方法可以获得变量指向的地址,由运行结果所示,一开始变量指向了113811696这个地址,这个地址存放了‘girls are pig’这个字符串,当变量发生改变时,即该变量的指向改变了,指向地址113812464,该地址存放有‘boys are dog’这个字符串。这两个字符串都是一开始解释器先在内存开辟好的。
      所以,这也就解释了为什么python的变量被整形赋值就成了整形,被列表赋值就成了列表,变量可以为任意数据类型的,因为python的变量只是对编译器事先在内存存放好的数据的引用
      python采用这种方式,好处就体现在,对于解释器来说,变量就只是一个地址的引用,而这个引用是可以随时改变的,那么就可以做到一个变量用来指向各种各样的数据类型,只要每次记录变量与哪个数据类型连接就行了,效率不就提升了嘛~。而对于c语言的编译器来说,一个变量就只能与一个数据类型长相厮守,所以它望着记录了各种各样变量名与内存值对应的表格,一边编译,一边陷入了沉思…(这里注意一点牛角尖,变量名只是给解释器看的东西,在内存是不做存储的,真正存储的是变量名对应的内容,上面说的变量都是int a中a这个个体

    2、复杂数据类型的存储方式

      这里说的复杂数据类型主要是像列表,字典等这种可以改变内部数据的数据类型。以列表作为例子举例,代码如下所示:

    list1 = [1,2,3]
    print(list1)			#输出:[1,2,3]
    print(id(list1))		#输出:112607104(不同电脑分配给变量的地址不同)
    list1[0] = "hello"
    print(list1)			#输出:['hello',2,3]
    print(id(list1))		#输出:112607104
    list1.append(4)
    print(list1)			#输出:['hello',2,3,4]
    print(id(list1))		#输出:112607104
    list1 = ['hello',4]
    print(list1)			#输出:['hello',4]
    print(id(list1))		#输出:112925120
    

      由运行结果所示,无论对列表list1进行什么增删改查操作,都不会影响list1本身的存储,只是改变了存储的内容,但list1重新赋值时,地址则发生改变。这个为了更好地解释清楚一点,就拿出我自豪的画画天赋吧(手动狗头,咳咳),上图~。
    在这里插入图片描述
      先声明一点,一个变量存有某一个对象的地址即等于该变量指向了这个对象。上面解释了,list1变量存放的是某个数据类型的引用,换种说法就是存放某个对象的地址,这里就是存放一个列表的地址,即list1变量指向了列表。如图所示,第一步,list1变量指向列表1,该列表存放着三个可变元素list1[0],list1[1],list1[2],它们分别存放着不同对象(值)的地址。第二步,列表的第一个元素list1[0]发生改变,变成存放hello这个字符串对象的地址。第三步,列表新增了一个元素,该元素存放了新的整形对象4的地址。第四步,列表变量list1重新赋值,指向了新的列表2,列表2元素又指向了hello和4这两个对象。
      因此,前面三步,因为都是改变了列表元素的指向,变量本身的指向没有变化,即变量的地址也没有变化,但第四步,变量进行重新的赋值,即指向了新的列表,那么变量的地址变发生了变化。
      这里也有重要的一点是,列表2和列表1指向的对象hello和4是一致的,因为它们的对象是一样的,所以它们共用一个对象。从下面代码可以体现,输出的结果是一致的。

    list1 = ['hello',2,3,4]
    print(id(list1[0]))		#输出:112926064
    print(id(list1[3]))		#输出:8791404644096
    list2 = ['hello',4]
    print(id(list2[0]))		#输出:112926064
    print(id(list2[1]))		#输出:8791404644096
    

    3、变量的赋值——浅拷贝和深拷贝

    (1)变量赋值的安全隐患

      因为python的这种变量是一个对象的引用的机制,必然导致的结果是两个变量赋值时会产生相互牵连的现象。举个例子,list1赋值为[1,2,3],然后将其赋值给list2,改变list1时,我们可以发现list2也发生改变。代码如下。

    list1 = [1,2,3]
    list2 = list1
    print(list1)			#输出:[1,2,3]
    print(list2)			#输出:[1,2,3]
    print(id(list1))		#输出:112607104
    print(id(list2))		#输出:112607104
    list1[0] = 'hello'
    print(list1)			#输出:['hello',2,3]
    print(list2)			#输出:['hello',2,3]
    print(id(list1))		#输出:112607104
    print(id(list2))		#输出:112607104
    

      解释图如下,第一步,list1变量指向了列表1,经过赋值后,变量list2也指向了列表1,因此两者地址相同。第二步,变量list1改变第一个列表元素的值,使其指向‘hello’,这时我们访问list2内容时,因为list1和list2指向的列表一致,所以list2就变成改变后的值。
    在这里插入图片描述
      由此引出主题深拷贝和浅拷贝,所谓深拷贝呢,就是一个变量的内容赋值给另一个变量时,是把全部资源重新复制一份再赋值给新的变量,而浅拷贝则不然,它的赋值只是将资源的地址给新的变量,二者同时共享该资源。显然,上面的赋值运算例子就是一个浅拷贝。

    (2)浅拷贝

      为了更加深入了解二者,举一个稍微复杂一丢丢的例子,这里我们需要用到外部包,copy包,它的方法copy()就是一个浅拷贝,而deepcopy()就是一个深拷贝。先举例浅拷贝,这次采用嵌套列表并且使用copy方法来进行拷贝。对比输出结果可以看到对列表list1和list2进行操作时,两者没影响,但对peope这个列表操作时,则两个列表都有影响。

    import copy
    people = ['girl','boy']
    list1 = [1,2,people]
    list2 = copy.copy(list1)
    print(list1)				#输出:[1,2,['girl','boy']]
    print(list2)				#输出:[1,2,['girl','boy']]
    list1.append('hello')		
    list2.append('hi')			
    print(list1)				#输出:[1,2,['girl','boy'],'hello']
    print(list2)				#输出:[1,2,['girl','boy'],'hi']
    people[0] = 'pig'
    print(list1)				#输出:[1,2,['pig','boy'],'hello']
    print(list2)				#输出:[1,2,['pig','boy'],'hi']
    

      由下图可知,第一步,list1和list2分别指向列表1和列表2,其元素也指向对应的值,但个列表的第三个元素都指向了同个列表。第二步,list1产生新元素,指向‘hello’,list2产生新的元素,指向‘hi’。第三步,people这个列表的第一元素地址指向从‘girl’变成了‘pig’(狗头保命),因为是共用列表,所以list1和list2这两个变量都产生了变化。从中也可以分析得到,copy这个方法不像‘=’这种赋值运算,它拷贝了资源的第一层,但如果有该资源有第二层时,则变成共用资源,这也是比较容易被忽略的一点。
    在这里插入图片描述
    (3)深拷贝

      为了解决浅拷贝带来的安全隐患,有时我们需要采用深拷贝来拷贝我们的资源。即python的copy模块提供的另一个deepcopy方法。深拷贝会完全复制原变量相关的所有数据,在内存中生成一堆一模一样的资源,在这个过程中我们对这两个变量中的一个进行任意修改都不会影响其他变量。我们来测试一下。

    import copy
    people = ['girl','boy']
    list1 = [1,2,people]
    list2 = copy.deepcopy(list1)
    print(list1)				#输出:[1,2,['girl','boy']]
    print(list2)				#输出:[1,2,['girl','boy']]
    list1.append('hello')		
    list2.append('hi')			
    print(list1)				#输出:[1,2,['girl','boy'],'hello']
    print(list2)				#输出:[1,2,['girl','boy'],'hi']
    people[0] = 'pig'
    print(list1)				#输出:[1,2,['pig','boy'],'hello']
    print(list2)				#输出:[1,2,['girl','boy'],'hi']
    

      流程如下图所示,其步骤和浅拷贝的步骤一致,但不同的一点是,步骤三的people列表改变时,只有list1变量的people列表中‘girl’变成‘pig’,而list2变量没什么影响,二者完全独立。
    在这里插入图片描述

    三、python变量数据类型的大小

      本来探索到上面已经差不多要结束,鬼知道我脑子又冒出了个奇怪的想法,python的int类型到底要占有电脑的多少个字节呢。毕竟习惯了c语言,而python对变量神奇的设计总是散发着它独特的魅力。所以找啊找,找到一个可以显示数据大小的API函数getsizeof(),只要导入sys包即可。那么写个例子:

    import sys
    print(sys.getsizeof(0))        # 输出:24
    print(sys.getsizeof(1))        # 输出:28 
    print(sys.getsizeof(2))        # 输出:28
    print(sys.getsizeof(2**15))    # 输出:28
    print(sys.getsizeof(2**30))    # 输出:32
    print(sys.getsizeof(2**128))   # 输出:44
    

      看到输出结果,属实让人震惊,一个int型的数值,居然用高达24个字节来存储,而且在电脑存储大小居然是不限定的,是自增长的。喝口水压压惊后,让我想到c++的STL容器,可以使用栈顶指针,当检测到容量超出时,则删除旧内存而去开辟一块新的内存,确实可以实现这种效果。
      扯完犊子,那么这里首先先解决第一个问题,int类型这个变量什么时候内存会变大?我在这篇博客中提到的文章找到了答案: 点此处跳转。重点就是下面这张图,简单来说就是int类型每多2^30(1073741824 )就会增加四个字节。这也验证了上面例子getsizeof(2**30)是32字节,而比它小的是28个字节的原因,当然零除外。其他类型也可以在下面找到答案。
    在这里插入图片描述
      那么它的自增长问题呢,这个可能要去看python的源码才能解决,还好有大佬已经提前给我们铺了下路,这里我就没这个能力去了解太深入了,直接引用大佬的结论就可以了。具体可以参考这篇文章:点此处跳转。在64位python的解释器中,int类型的定义是通过一个结构体来定义的,简化后的结构体如下所示:

    struct PyLongObject {
      long ob_refcnt;                // 8 bytes
      struct _typeobject *ob_type;   // 8 bytes
      long ob_size;                  // 8 bytes
      unsigned int ob_digit[1];      // 4 bytes * abs(ob_size)
    };
    

      ob_refcnt引用计数8个字节,ob_type类型信息8个字节(指针),ob_size变长部分元素的个数8个字节。ob_digit变长的数据部分,字节数为4 * abs(ob_size),ob_size可以为0,所以ob_digit这部分可以占0字节,那么最少int就为8 + 8 + 8 = 24个字节,每次增量都是4(unsigned int)的倍数。
      对于32位的版本与64位又有所不同,定义如下,最少12个字节,增量为2个字节。

    struct PyLongObject {
      int ob_refcnt;                // 4 bytes
      struct _typeobject *ob_type;  // 4 bytes
      int ob_size;                  // 4 bytes
      unsigned short ob_digit[1];   // 2 bytes * abs(ob_size)
    };
    

      至于其他类型实际大小,也是一个类似的方案,这里也不探讨太多东西了,学无止境吧~

    总结

      这篇文章从变量的角度切入,首先谈谈什么是变量的类型,并且举例了python中常用的基本数据类型,接着讨论了变量在内存中的存储,说白了就一句话,变量就是某一个对象的引用,对象在内存爱怎么放怎么放与变量无关。最后讨论了int类型占有电脑的字节数。


    Tips:本人能力有限,如有错误之处麻烦指出。放弃不难,但坚持一定很酷!
    展开全文
  • 知道数据类型就要知道变量数据类型怎么存储,可是为什么python的变量不需要声明数据类型就可以直接赋值?变量如果有数据类型,那变量不是可以为任意数据类型?那真正的数据类型如int在内存存储的字节大小应该为多少...

    我们知道,python的变量是有类型的,对于python变量的几种数据类型,我们在写python时是必须要有一定的概念的。知道数据类型就要知道变量数据类型怎么存储,可是为什么python的变量不需要声明数据类型就可以直接赋值?变量如果有数据类型,那变量不是可以为任意数据类型?那真正的数据类型如int在内存存储的字节大小应该为多少?等等诸如一系列的问题让我提起了的兴趣,经过网上不断查找学习后,在此将我所了解到的内容在此做个总结归纳

    一、变量的数据类型

    1、什么是变量的数据类型

    我们先捋一捋什么是变量,变量从字面上理解就是可以变化的量,我们可以随时改变这个变量的值,使得我们可以调用同一个变量而获得不同的值,与之对应的是常量。那么对于一个可变的变量,它有可能表示是一个字符串,一个数字或者是一个小数,因为这些在计算机内存里存放的方式是不一样的,所以简单理解就是变量的数据类型不同就是对应的数据在计算机内存中存放方式的不同。这种方式表现在按多少字节存储,是否连续存储等。

    我们都知道,c是静态类型语言,一种在编译期间就确定数据类型的语言,也就是我们需要对变量先声明其数据类型后才能使用,并且在使用过程中一般不能赋值一些超过该数据类型数值,比如:int a = 1.2,当然大类型是可以转向小类型的,如:double a = 1 (double类型接收整形数值)。可以肯定的,大多数静态类型语言都这么干。

    当然,python语言也有数据类型。但python语言不同,它是一种动态类型语言,又是强类型语言。它们确定一个变量的数据类型是在你第一次给它赋值的时候,也就是说你赋值给变量什么数据类型的数值,变量就是什么数据类型的。所以,对比之下,c语言变量的数据类型是事先定义的,而python是后天接受的。

    2、python五大标准数据类型

    在讲变量存储之前,这里先简单总结下python的五大标准数据类型,为了方便展示,我们采用type方法显示变量的数据类型。

    (1)Numbers(数字)

    数字数据类型用于存储数值。他们是不可改变的数据类型,可简单分为以下四种:(注意这里十六进制,八进制都属于int整形。)

    int(整型):

    var = 520

    print(type(var)) #

    float(浮点型):

    var = 5.20

    print(type(var)) # 输出:

    bool(布尔型):

    var = true

    print(type(var)) # 输出:

    complex(复数):

    var = complex(13,14)

    print(type(var)) # 输出:

    (2)String(字符串)

    字符串或串是由数字、字母、下划线组成的一串字符,用‘',“”,“‘ '”都可表示。三者的使用可参考这篇文章: python字符串的各种表达方式.

    如下方代码所示,获得的类型为str类型。另外也顺便提一个小知识点,要访问字符串可以正向访问也可以反向访问,即正向时,var[0] = ‘p',var[1] = ‘i',var[2] = ‘g';而反向时,var[-1] = ‘g',var[-2] = ‘i',var[-3] = ‘p'。

    var = “pig”

    print(type(var)) # 输出:

    print(var[0:3]) # 正向访问,输出:'pig'

    print(var[-1]) # 反向访问,输出:'g'

    (3)List(列表)

    列表是 Python 中使用最频繁的数据类型,用 [ ] 标识。列表可以完成大多数集合类的数据结构实现。它可以同时包含字符,数字,字符串甚至可以包含列表(即嵌套)。如下方代码所示,列表的处理方式和字符串类似。

    var = [ 'pig' , 1 , 2.2 ]

    print(type(var)) # 输出:

    print(var[0]) # 获得第一个元素,输出:'pig'

    print(var+var) # 打印组合的列表,输出:[ 'pig', 1 , 2.2,'pig', 1 , 2.2 ]

    (4)Tuple(元组)

    元组类似于 List(列表)。元组用 () 标识。内部元素用逗号隔开。但是元组不能二次赋值,相当于只读列表。

    var = ( 'pig', 1 , 2.2 )

    print(type(var)) # 输出:

    print(var[0]) # 获得第一个元素,输出:'pig'

    print(var+var) # 打印组合的元组,输出:( 'pig', 1 , 2.2,'pig', 1 , 2.2 )

    var[0] = 'dog' # 出错!不能被二次赋值

    (5)Dictionary(字典)

    字典的相对于列表来说,列表是有序的对象集合,而字典是无序的对象集合。两者之间的区别在于字典当中的元素是通过键来存取的,而不是通过偏移存取。字典用"{ }"标识,字典由索引key和它对应的值value组成。

    dic = {'name':'张三','age':18}

    print(dic ['name']) # 得到键为'name' 的值,输出:'张三'

    print(dic [age]) # 得到键为'age' 的值,输出:18

    print(dic) # 得到完整的字典,输出:{'name':'张三','age':18}

    print(dic.keys()) # 得到所有键,输出:dict_keys:(['name','age'])

    print(dic.values()) # 输出所有值,输出:dict_values:(['张三',18])

    二、python变量的存储

    1、变量与储存地址的关系

    在高级语言中,变量是对内存及其地址的抽象。以c语言举例, 变量事先定义好一种数据类型,于是编译器为变量分配一个对应类型的地址空间和大小(如int 4字节,char 1字节),当该变量改变值时,改变的只是这块地址空间中保存的值,即在程序运行中,变量的地址就不能再发生改变了。这种存储方式称为值语义。如下代码用VS2015运行,由结果可知,test变量的值被存储在0x0020FDC8,当变量改变时,地址不变,地址中对应的值发生改变。

    #include

    using namespace std;

    int main()

    {

    int test = 1;

    cout << &test << ":" << test << endl;

    test = 2;

    cout << &test << ":" << test << endl;

    return 0;

    }

    运行结果:

    0020FDC8:1

    0020FDC8:2

    这里就存在一个问题,每次新建一个变量,编译器就会开辟一块对应数据类型大小的内存,然后给那块内存取个名字(变量名)。除非一块内存被释放,那么该变量才能释放,不然一个变量就只能固定地对应一个数据类型。

    对此,python做出了改变,它采用了与高级语言截然不同的方式。在python中,一切变量都是对象,变量的存储采用了引用语义的方式,存储的只是一个变量的值所在的内存地址,而不是这个变量的值本身。简单理解就是,python变量只是某个数据的引用(可以理解成C语言的指针),当python变量赋值时,解释器(因为python为解释性语言)先为数值开辟一块空间,而变量则指向这块空间,当变量改变值时,改变的并不是这块空间中保存的值,而是改变了变量的指向,使变量指向另一个地址空间。这种存储方式称为对象语义或指针语义。举个例子:

    str = 'girls are pig'

    print(id(str))

    str = 'boys are dog'

    print(id(str))

    运行结果:

    113811696

    113812464

    id()方法可以获得变量指向的地址,由运行结果所示,一开始变量指向了113811696这个地址,这个地址存放了‘girls are pig'这个字符串,当变量发生改变时,即该变量的指向改变了,指向地址113812464,该地址存放有‘boys are dog'这个字符串。这两个字符串都是一开始解释器先在内存开辟好的。

    所以,这也就解释了为什么python的变量被整形赋值就成了整形,被列表赋值就成了列表,变量可以为任意数据类型的,因为python的变量只是对编译器事先在内存存放好的数据的引用。

    python采用这种方式,好处就体现在,对于解释器来说,变量就只是一个地址的引用,而这个引用是可以随时改变的,那么就可以做到一个变量用来指向各种各样的数据类型,只要每次记录变量与哪个数据类型连接就行了,效率不就提升了嘛~。而对于c语言的编译器来说,一个变量就只能与一个数据类型长相厮守,所以它望着记录了各种各样变量名与内存值对应的表格,一边编译,一边陷入了沉思…(这里注意一点牛角尖,变量名只是给解释器看的东西,在内存是不做存储的,真正存储的是变量名对应的内容,上面说的变量都是int a中a这个个体)

    2、复杂数据类型的存储方式

    这里说的复杂数据类型主要是像列表,字典等这种可以改变内部数据的数据类型。以列表作为例子举例,代码如下所示:

    list1 = [1,2,3]

    print(list1)#输出:[1,2,3]

    print(id(list1))#输出:112607104(不同电脑分配给变量的地址不同)

    list1[0] = "hello"

    print(list1)#输出:['hello',2,3]

    print(id(list1))#输出:112607104

    list1.append(4)

    print(list1)#输出:['hello',2,3,4]

    print(id(list1))#输出:112607104

    list1 = ['hello',4]

    print(list1)#输出:['hello',4]

    print(id(list1))#输出:112925120

    由运行结果所示,无论对列表list1进行什么增删改查操作,都不会影响list1本身的存储,只是改变了存储的内容,但list1重新赋值时,地址则发生改变。这个为了更好地解释清楚一点,就拿出我自豪的画画天赋吧(手动狗头,咳咳),上图~。

    44ebcfc35d1086227bc8ec2bdd7694b0.png

    先声明一点,一个变量存有某一个对象的地址即等于该变量指向了这个对象。上面解释了,list1变量存放的是某个数据类型的引用,换种说法就是存放某个对象的地址,这里就是存放一个列表的地址,即list1变量指向了列表。如图所示,第一步,list1变量指向列表1,该列表存放着三个可变元素list1[0],list1[1],list1[2],它们分别存放着不同对象(值)的地址。第二步,列表的第一个元素list1[0]发生改变,变成存放hello这个字符串对象的地址。第三步,列表新增了一个元素,该元素存放了新的整形对象4的地址。第四步,列表变量list1重新赋值,指向了新的列表2,列表2元素又指向了hello和4这两个对象。

    因此,前面三步,因为都是改变了列表元素的指向,变量本身的指向没有变化,即变量的地址也没有变化,但第四步,变量进行重新的赋值,即指向了新的列表,那么变量的地址变发生了变化。

    这里也有重要的一点是,列表2和列表1指向的对象hello和4是一致的,因为它们的对象是一样的,所以它们共用一个对象。从下面代码可以体现,输出的结果是一致的。

    list1 = ['hello',2,3,4]

    print(id(list1[0]))#输出:112926064

    print(id(list1[3]))#输出:8791404644096

    list2 = ['hello',4]

    print(id(list2[0]))#输出:112926064

    print(id(list2[1]))#输出:8791404644096

    3、变量的赋值——浅拷贝和深拷贝

    (1)变量赋值的安全隐患

    因为python的这种变量是一个对象的引用的机制,必然导致的结果是两个变量赋值时会产生相互牵连的现象。举个例子,list1赋值为[1,2,3],然后将其赋值给list2,改变list1时,我们可以发现list2也发生改变。代码如下。

    list1 = [1,2,3]

    list2 = list1

    print(list1)#输出:[1,2,3]

    print(list2)#输出:[1,2,3]

    print(id(list1))#输出:112607104

    print(id(list2))#输出:112607104

    list1[0] = 'hello'

    print(list1)#输出:['hello',2,3]

    print(list2)#输出:['hello',2,3]

    print(id(list1))#输出:112607104

    print(id(list2))#输出:112607104

    解释图如下,第一步,list1变量指向了列表1,经过赋值后,变量list2也指向了列表1,因此两者地址相同。第二步,变量list1改变第一个列表元素的值,使其指向‘hello',这时我们访问list2内容时,因为list1和list2指向的列表一致,所以list2就变成改变后的值。

    81f7890d925770eb87afb97f3535e8ca.png

    由此引出主题深拷贝和浅拷贝,所谓深拷贝呢,就是一个变量的内容赋值给另一个变量时,是把全部资源重新复制一份再赋值给新的变量,而浅拷贝则不然,它的赋值只是将资源的地址给新的变量,二者同时共享该资源。显然,上面的赋值运算例子就是一个浅拷贝。

    (2)浅拷贝

    为了更加深入了解二者,举一个稍微复杂一丢丢的例子,这里我们需要用到外部包,copy包,它的方法copy()就是一个浅拷贝,而deepcopy()就是一个深拷贝。先举例浅拷贝,这次采用嵌套列表并且使用copy方法来进行拷贝。对比输出结果可以看到对列表list1和list2进行操作时,两者没影响,但对peope这个列表操作时,则两个列表都有影响。

    import copy

    people = ['girl','boy']

    list1 = [1,2,people]

    list2 = copy.copy(list1)

    print(list1)#输出:[1,2,['girl','boy']]

    print(list2)#输出:[1,2,['girl','boy']]

    list1.append('hello')

    list2.append('hi')

    print(list1)#输出:[1,2,['girl','boy'],'hello']

    print(list2)#输出:[1,2,['girl','boy'],'hi']

    people[0] = 'pig'

    print(list1)#输出:[1,2,['pig','boy'],'hello']

    print(list2)#输出:[1,2,['pig','boy'],'hi']

    由下图可知,第一步,list1和list2分别指向列表1和列表2,其元素也指向对应的值,但个列表的第三个元素都指向了同个列表。第二步,list1产生新元素,指向‘hello',list2产生新的元素,指向‘hi'。第三步,people这个列表的第一元素地址指向从‘girl'变成了‘pig'(狗头保命),因为是共用列表,所以list1和list2这两个变量都产生了变化。从中也可以分析得到,copy这个方法不像‘='这种赋值运算,它拷贝了资源的第一层,但如果有该资源有第二层时,则变成共用资源,这也是比较容易被忽略的一点。

    af7337bf9dc7f4b8505e21c19abe09ad.png

    (3)深拷贝

    为了解决浅拷贝带来的安全隐患,有时我们需要采用深拷贝来拷贝我们的资源。即python的copy模块提供的另一个deepcopy方法。深拷贝会完全复制原变量相关的所有数据,在内存中生成一堆一模一样的资源,在这个过程中我们对这两个变量中的一个进行任意修改都不会影响其他变量。我们来测试一下。

    import copy

    people = ['girl','boy']

    list1 = [1,2,people]

    list2 = copy.deepcopy(list1)

    print(list1)#输出:[1,2,['girl','boy']]

    print(list2)#输出:[1,2,['girl','boy']]

    list1.append('hello')

    list2.append('hi')

    print(list1)#输出:[1,2,['girl','boy'],'hello']

    print(list2)#输出:[1,2,['girl','boy'],'hi']

    people[0] = 'pig'

    print(list1)#输出:[1,2,['pig','boy'],'hello']

    print(list2)#输出:[1,2,['girl','boy'],'hi']

    流程如下图所示,其步骤和浅拷贝的步骤一致,但不同的一点是,步骤三的people列表改变时,只有list1变量的people列表中‘girl'变成‘pig',而list2变量没什么影响,二者完全独立。

    1dcb631ad36c5aa025da8c4af6bdca91.png

    三、python变量数据类型的大小

    本来探索到上面已经差不多要结束,鬼知道我脑子又冒出了个奇怪的想法,python的int类型到底要占有电脑的多少个字节呢。毕竟习惯了c语言,而python对变量神奇的设计总是散发着它独特的魅力。所以找啊找,找到一个可以显示数据大小的API函数getsizeof(),只要导入sys包即可。那么写个例子:

    import sys

    print(sys.getsizeof(0)) # 输出:24

    print(sys.getsizeof(1)) # 输出:28

    print(sys.getsizeof(2)) # 输出:28

    print(sys.getsizeof(2**15)) # 输出:28

    print(sys.getsizeof(2**30)) # 输出:32

    print(sys.getsizeof(2**128)) # 输出:44

    看到输出结果,属实让人震惊,一个int型的数值,居然用高达24个字节来存储,而且在电脑存储大小居然是不限定的,是自增长的。喝口水压压惊后,让我想到c++的STL容器,可以使用栈顶指针,当检测到容量超出时,则删除旧内存而去开辟一块新的内存,确实可以实现这种效果。

    扯完犊子,那么这里首先先解决第一个问题,int类型这个变量什么时候内存会变大?我在这篇博客中提到的文章找到了答案: 点此处跳转。重点就是下面这张图,简单来说就是int类型每多2^30(1073741824 )就会增加四个字节。这也验证了上面例子getsizeof(2**30)是32字节,而比它小的是28个字节的原因,当然零除外。其他类型也可以在下面找到答案。

    274b97c0cbb0c1fccb48ea9bc188b65e.png

    那么它的自增长问题呢,这个可能要去看python的源码才能解决,还好有大佬已经提前给我们铺了下路,这里我就没这个能力去了解太深入了,直接引用大佬的结论就可以了。具体可以参考这篇文章:点此处跳转。在64位python的解释器中,int类型的定义是通过一个结构体来定义的,简化后的结构体如下所示:

    struct PyLongObject {

    long ob_refcnt; // 8 bytes

    struct _typeobject *ob_type; // 8 bytes

    long ob_size; // 8 bytes

    unsigned int ob_digit[1]; // 4 bytes * abs(ob_size)

    };

    ob_refcnt引用计数8个字节,ob_type类型信息8个字节(指针),ob_size变长部分元素的个数8个字节。ob_digit变长的数据部分,字节数为4 * abs(ob_size),ob_size可以为0,所以ob_digit这部分可以占0字节,那么最少int就为8 + 8 + 8 = 24个字节,每次增量都是4(unsigned int)的倍数。

    对于32位的版本与64位又有所不同,定义如下,最少12个字节,增量为2个字节。

    struct PyLongObject {

    int ob_refcnt; // 4 bytes

    struct _typeobject *ob_type; // 4 bytes

    int ob_size; // 4 bytes

    unsigned short ob_digit[1]; // 2 bytes * abs(ob_size)

    };

    至于其他类型实际大小,也是一个类似的方案,这里也不探讨太多东西了,学无止境吧~

    总结

    这篇文章从变量的角度切入,首先谈谈什么是变量的类型,并且举例了python中常用的基本数据类型,接着讨论了变量在内存中的存储,说白了就一句话,变量就是某一个对象的引用,对象在内存爱怎么放怎么放与变量无关。最后讨论了int类型占有电脑的字节数。

    Tips:本人能力有限,如有错误之处麻烦指出。放弃不难,但坚持一定很酷!

    到此这篇关于深入理解Python变量的数据类型和存储的文章就介绍到这了,更多相关Python变量的数据类型和存储内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

    展开全文
  • 在用jupyter notebook写python代码的过程中会产生很多变量,而关闭后或者...2.导入包后即可开始实质性操作,我们定义保存变量和读取变量的函数。 保存变量函数: def save_variable(v,filename): f=open(filename,
  • 现实生活中,我们在超市买东西...在python中,若要存储数据,需要用到变量变量可以理解为去超市购物使用购物车,他的类型值在赋值的那一刻被初始化。变量的赋值是通过符号来表示的,示例代码如下:num_one = 100...
  • 现实生活中,我们在超市买东西的...在python中,若要存储数据,需要用到变量变量可以理解为去超市购物使用购物车,他的类型值在赋值的那一刻被初始化。变量的赋值是通过符号来表示的,示例代码如下:num_one=100...
  • Python变量和数据

    2018-10-23 15:20:33
    变量 数据 是分开存储数据 保存在内存中的一个位置 变量保存数据在内存中的地址 变量 中 记录数据的地址,就叫做 引用 使用 id() 函数可以查看变量保存数据所在的 内存地址 """ 引用 ...
  • 1. Python变量的赋值 在编程语言中,将数据放入变量的过程叫做赋值(Assignment)。每个变量在使用前都必须赋值,变量赋值以后,该变量才会被创建。 Python 使用等号=作为赋值运算符,具体格式为: name = value # ...
  • python变量 数据类型 列表 元组 字典变量数据类型数据拼接列表添加列表元素修改元素删除列表元素组织列表确定列表长度创建数值列表操作列表元组元组拼接元组转列表字典创建字典列表取值字典删除增加修改 变量 变量...
  • 因此,通过分配不同的数据类型的变量,你可以存储整数,小数或字符在这些变量中。 变量赋值: Python变量不必显式地声明保留的存储器空间。当分配一个值给一个变量的声明将自动发生。等号(=)来赋值给变量。 操作数...
  • 【Python学习】【数据结构】之链表(python变量标识本质、链表操作)链表Python变量标识本质链表操作 链表 一个简单的链表形式如下: 一个节点分为数据链接区,数据存储数据好说,而链接区需要的是存储地址,...
  • 对于python而言,python的一切变量都是对象,变量存储,采用了引用语义的方式,存储的只是一个变量的值所在的内存地址,而不是这个变量的只本身。 引用语义:在python中,变量保存的是对象(值)的引用,我们称为...
  • python变量 对象 python变量 在python中变量是地址(在c语言中就是指 指针),比如说,有一个变量m,那么在内存中有一块区域名字是m,m是内存的标志,在这个区域中存储的 内容是一个 指针,我理解这是一个常量。 ...
  • 存储数据,处理数据数据存储位置?数据存在内存中内存:用于暂时存储被处理的数据内存中数据的存储方式:二进制的形式存储数据抽象解释:0 1 一种开关,两种状态单位: 8bit(位) = 1byte(字节)1024byte = 1KB1024KB ...
  • 一、python变量  说起变量我相信大家应该都不会陌生,毕竟我们都是学过一些或者了解过一些语言的人。如果还是不明白那我就解释一下何为变量。变量就是可以随时改变的量,变量就是一个存储数据的内存空间对象。定义...
  • 图解Python变量与赋值

    2020-12-24 02:05:26
    Python是一门独特的语言,与C语言有很大区别,初学Python很多萌新表示对变量与赋值不理解,学过C的都知道,给变量赋值时,需要先指定数据类型,同时会开辟一块内存区域,用于存储值,例如: int a = 1; a 就是内存...
  • Python变量和数据类型

    千次阅读 多人点赞 2020-05-11 22:20:23
    在上一篇《Python语言概述》中简单的介绍了一些关于Python的特性,包括Python的主要应用领域、...我们将在这一篇学习可在Python程序中使用的各种数据,还将学习如何将数据存储变量中,以及如何在程序中使用这些变量
  • 2-1 简单消息:将一条消息存储变量中,再打印出来。message = "I am a student." print(message)输入以上代码后执行,可以看到输出如下:I am a student.2-2 多条简单消息:将一条消息存储变量,打印...
  • Python变量存储

    2020-11-20 23:35:59
    1在python中,变量保存的是对象(值)的引用,我们称为引用语义。采用这种方式,变量所需的存储空间大小一致,因为变量只是保存了一个引用。也被称为对象语义指针语义。变量的每一次初始化,都开辟了一个新的空间,...
  • 为大家分享一下python读取和保存图片5种方法与比较,python中对象之间的赋值是按引用传递的,如果需要拷贝对象,需要用到标准库中的copy模块
  • 如何将python中的变量保存在本地?将python 的一些代码保存在本地, 特别是一些需要大量运算的结果,例如 机器学习里面的模型,,放在本地,还是比较好用的。下次就可以直接拿出来使用就好。其实可以 我觉得可以把 ...
  • Python变量是编程中无法绕开的一个知识点,只有有了Python变量,代码才能正常运作,而值,则尤为重要。变量存储在内存中的值,这就意味着在创建变量时会在内存中开辟一个空间,基于变量的数据类型,解释器会分配指定...
  • 其中,Python只有“local”、“global”“nonlocal”变量。在其中一些存储在字典或类似字典的对象中,通常可以显式寻址。在“全局”:实际上,“全局”变量相对于定义它们的模块是全局的-有时它们被称为“模块级”...
  • python如何保存变量

    万次阅读 2018-08-05 21:38:31
    在MATLAB中,保存和加载变量是非常简单的事情,使用一对save和load就可以。而对于python,操作就变得有些复杂了,一个文件似乎也只能保存一个变量。 import pickle # 存储变量的文件的名字 filename = 'shoplist....
  • 1.python数据类型可以分为 数字型 非数字型。 数字型 整型 (int) 浮点型(float) 布尔型(bool) 复数型 (complex)(主要用于科学计算) 非数字型 字符串 列表 元组 字典 2.不同类型变量之间的运算问题: ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 285,670
精华内容 114,268
关键字:

python变量和数据的存储

python 订阅