python小结2:标识符和变量
本篇主要讲解:标识符的规则和规范;变量和对象的区别;
一、标识符
标识符:用于变量、函数、类、模块等的名称。
1.1标识符有如下特定的规则:
1. 区分大小写。如:ABC 和 abc 是不同的
2. 第一个字符必须是字母、下划线。其后的字符是:字母、数字、下划线
争议:由于很多国家的人不熟悉英语,所以在PEP 3131中提出了标识符支持非 ASCII 字母(如重音字符、西里尔文、希腊语、汉字等)。
所以我们可以使用中文作为标识符。但是也有争议,有人认为一旦非 ASCII 字母作为标识符的行为得到了推广,以后会对开源以及不同国家的人之间的合作造成较大的影响。比如不会中文的人,不安装中文输入法,不会中文,根本无法输入中文。所以,目前虽然python3已经支持中文作为标识符,但是考虑到上面的问题,也考虑到与python2的兼容,一般都不推荐将中文作为标识符。当然如果你的项目只作为个人使用,这个是不受限制的。
3. 不能使用关键字。比如:
if、or、while 等。
4.Python中的标识符中,不能包含空格、@、% 以及 $ 等特殊字符。
1.2关键字和函数的区别
我们可以使用 Python 帮助系统查看关键字
>>> help()
help> keywords
通俗讲:
关键字是构成python的重要基础,它不允许同名标识符的存在。
函数是“行为”的概括,它可以被同名标识符覆盖。
比如:关键词的while、for、bool、or都有特定的意义,如果能被覆盖,那么python就不再支持循环和判断。这样的语言可以说是“残废”。python不可能为了满足你的需求,而直接废掉自己作为一门编程语言的基础,所以标识符与关键字同名会直接报错。
而函数的覆盖就不会报错。
1.3标识符规范:
1.以单下划线开头或者双下划线开头的标识符,比如“_a”或“__a”表示“私有”,其无法通过 from...import* 的方式导入;
比如我们创建一个test0.py,里面放上两个变量。
我们在另一个文件中运行下面内容:
from test0 import *
print(a)
print(_a)
print(__a)
结果:100 错误 错误
from XX import * 我们无法访问私有内容。
import test0
print(test0.a)
print(test0._a)
print(test0.__a)
结果:100 100 100
import *的情况下,我们可以访问私有内容。但是我们需要承担同事或者代码分享者在不通知你的情况下修改了内容的后果。
2.以双下划线开头和结尾的标识符,比如“__XX__”。我们应该尽量避免,因为它通常是类的构造函数。
比如我们可以通过对类添加相应的方法,使它支持数学运算。
3.常量命名应全部使用大写字母,单词之间可以用下划线分割;
这一项不是python语言特性,只是作为一种规范。python并不阻止我们修改全大写的变量。
但是,既然是一种规范,我们就应该知道这个变量应该是不能随便修改的,避免在不知情的情况下导致一些不必要的错误。
4.函数名、类中的属性名和方法名,应全部使用小写字母,多个单词之间可以用下划线分割
5.避免关键词冲突。如果你很想用关键词作为变量,那么在后面添加一个下划线后缀。比如"pass_"
6.类名第一个字母大写。
7.类的私有对象。前面加双下划线。
比如:
就在本文件运行下面代码:
p1 = People('张三')
print(p1._height)
-->>180
print(p1.name)
-->>张三
print(p1.__age)
-->>错误
设置了类的私有属性(双下划线开头),我们是不可以访问的,就算是在本地文件。
我们可以通过类的方法取修改相应的属性。比如:
p1 = People('张三')
print(p1)
p1.set_age(36)
print(p1)
结果:
姓名:张三;年龄:18;身高:180
姓名:张三;年龄:36;身高:180
8.标识符应该具有一定的可读性。允许用可读性换取标识符的长度。
简单而言:就算用很长的标识符,也比用看不明白意义的标识符要好。
如果很纠结取名,可以参考网站:
https://unbug.github.io/codelf
9.允许使用缩写。
function 缩写为 fn
object 缩写为 obj
count 缩写为 cnt
number 缩写为 num
缩写尽量选择可读性较好的,如果可读性差,那么宁愿不使用缩写。
可以参考文章,具体是否使用缩写根据情况而定:
总结:
标识符中可以包含字母、下划线,但需以字母或者下划线开头;
常量全大写;
类名首字母大写;
函数名、变量名、类的属性和方法名全小写;
不要以双下划线或者单下划线开头,除非你知道它的影响;
标识符可读性比标识符的长短更重要。
二、变量
2.1变量和对象
Python 中,一切皆对象。
对象:每个对象由:ID、类型、内容组成。
变量:对象的引用。变量通过地址引用了“对象”,变量存储的就是对象的地址。
我们对变量的操作,实际上是对引用对象的操作。可变和不可变说的是”对象“。
比如:
a= [1,2,3]
b= a
c = a
b.append(4)
c = c + [4]
print("列表a",a)
print("列表b",b)
print("列表c",c)
结果:
列表a [1, 2, 3, 4]
列表b [1, 2, 3, 4]
列表c [1, 2, 3, 4, 4]
过程:
这里,a、b、c最开始都引用[1,2,3]。对象末尾添加了4。.append(4)
a、b、c所引用的对象都是[1,2,3,4]了。这就是可变对象内容改变对多个引用变量造成的影响。
然后c = c + [4] ,是[1,2,3,4]+[4]的计算,[1,2,3,4]和[4]这两个对象都没有改变。然后结果写入一个新的地址,赋值就是变量c引用这个新的对象。
2.2动态语言
python是动态语言。变量不需要显式声明类型。根据变量引用的对象,Python 解释器自动确定数据类型。
优点:灵活,限制小
缺点:对代码的修改和维护困难。因为时间久了之后,你已经忘记变量引用的对象是经历什么运算而转化过来的。稍微的修改,就可能造成错误。你需要反复的阅读上下文,反复调试和推导,以确保你的修改造成的影响是你能把控的。
2.3变量的赋值
变量的赋值是建立对对象的引用。例如前面列表的举例。
对对象的操作,会影响所有被引用变量的值。
2.3.1赋值和深浅拷贝
1.赋值: 只是建立了对象的引用
变量的值的改变,两种情况:
要么是因为引用对象本身改变了(可变对象),就比如前面列表进行.append()的例子。
要么是因为重新建立了新的引用。比如:
2.浅拷贝
拷贝父对象,不会拷贝对象的内部的子对象。
3.深拷贝
copy 模块的 deepcopy 方法,完全拷贝了父对象及其子对象。
2.3.2赋值和深浅拷贝的区别
随便举个例子。
lst = ["从零到放弃", 509, [1, 2, 3]]
a = lst
b = copy.copy(lst ) #浅拷贝
c= copy.deepcopy(lst ) #深拷贝
lst和a、b、c的内存建立和存储状态见上图。
例子1:
lst = ["从零到放弃", 509, [1, 2, 3]]
a = lst
b = copy.copy(lst ) #浅拷贝
c= copy.deepcopy(lst ) #深拷贝
lst.append(100)
print(lst)
print(a)
print(b)
print(c)
结果:
['从零到放弃', 509, [1, 2, 3], 100]
['从零到放弃', 509, [1, 2, 3], 100]
['从零到放弃', 509, [1, 2, 3]]
['从零到放弃', 509, [1, 2, 3]]
因为c、d拷贝了外部,所以lst进行改变,不会影响c、d
lst改变的过程如图。
例子2:
lst = ["从零到放弃", 509, [1, 2, 3]]
a = lst
b = copy.copy(lst ) #浅拷贝
c= copy.deepcopy(lst ) #深拷贝
lst[2].append(100)
print(lst)
print(a)
print(b)
print(c)
结果:
['从零到放弃', 509, [1, 2, 3, 100]]
['从零到放弃', 509, [1, 2, 3, 100]]
['从零到放弃', 509, [1, 2, 3, 100]]
['从零到放弃', 509, [1, 2, 3]]
因为c、d拷贝了外部,但是c没有内部拷贝,所以lst[2]的改变影响了c。d不受影响。
要理解深浅拷贝的过程影响,就必须知道可变对象和不可变对象的区别。
例子3:
lst = ["从零到放弃", 509, [1, 2, 3]]
a = lst
b = copy.copy(lst ) #浅拷贝
c= copy.deepcopy(lst ) #深拷贝
lst[2] = 0
print(lst)
print(a)
print(b)
print(c)
结果:
['从零到放弃', 509, 0]
['从零到放弃', 509, 0]
['从零到放弃', 509, [1, 2, 3]]
['从零到放弃', 509, [1, 2, 3]]
2.3.3解压赋值
1.解压赋值
将一个可迭代对象的拆解后,一并赋值给多个变量的过程,就是解压赋值。
其中有两个注意点。
*变量 :匹配多个(0个或1个)值 。结果保存为列表
_ :忽略某个值
*_:忽略多个(0个或1个)值
用案例解释
a = "从零到放弃"
b,c,d,e,f = a
print(b,c,d,e,f)
-->从 零 到 放 弃
b,c,d,e,f,*g = a
print(b,c,d,e,f,g)
-->从 零 到 放 弃 []
*b,c,d = a
print(b,c,d)
-->['从', '零', '到'] 放 弃
b,*c,d = a
print(b,c,d)
-->从 ['零', '到', '放'] 弃
b,_,_,c,_ = a
print(b,c)
-->从 放
b,_,*_,c,_ = a
print(b,c)
-->从 放
列表、集合、元组的用法类似。
只需要注意字典,匹配的值都是字典的键。
a = {"学":1,"习":2,"好":3}
b,c,d = a
print(b,c,d)
-->学 习 好
2.变量之间逗号的意义
a = "学习"
b = "python"
c = 100
d = a,b,c
print(d)
-->('学习', 'python', 100)
a,b,c赋值的时候就是(a,b,c)
所以我们可以用a,b =b,a的方式去进行变量值的交换。