標簽:print info 語言 流程 system 拼接 管理 支持 __name__
文章目錄
8.1 可变参数默认值设为`None`
8.2 对子类继承的变量要做显式定义和赋初值
8.3 严禁使用注释行等形式仅使功能失效
8.4 慎用`copy`和 `deepcopy`
8.5 系统路径推荐使用 `pathlib.Path`
8.6 使用`subprocess`模块代替`os.system`模块来执行`shell`命令
8.7 建议使用with语句操作文件
8.1 可变参数默认值设为None
函數參數中的可變參數不要使用默認值,在定義時使用None
說明:參數的默認值會在方法定義被執行時就已經設定了,這就意味著默認值只會被設定一次,當函數定義後,每次被調用時都會有"預計算"的過程。當參數的默認值是一個可變的對象時,就顯得尤爲重要,例如參數值是一個list或dict,如果方法體修改這個值(例如往list裏追加數據),那麽這個修改就會影響到下一次調用這個方法,這顯然不是一種好的方式。應對種情況的方式是將參數的默認值設定爲None。
錯誤示例:
>>> def foo(bar=[]): # bar is optional and defaults to [] if not specified
... bar.append("baz") # but this line could be problematic, as we‘ll see...
... return bar
1
2
3
在上面這段代碼裏,一旦重複調用foo()函數(沒有指定一個bar參數),那麽將一直返回’bar’。因爲沒有指定參數,那麽foo()每次被調用的時候,都會賦予[]。下面來看看,這樣做的結果:
>>> foo()
["baz"]
>>> foo()
["baz", "baz"]
>>> foo()
["baz", "baz", "baz"]
# 正确示例:None是不错的选择
>>> def foo(bar=None):
... if bar is None: # or if not bar:
... bar = []
... bar.append("baz")
... return bar
...
>>> foo()
["baz"]
>>> foo()
["baz"]
>>> foo()
["baz"]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
8.2 对子类继承的变量要做显式定义和赋初值
說明:在Python中,類變量都是作爲字典進行內部處理的,並且遵循方法解析順序(MRO)。子類沒有定義的屬性會引用基類的屬性值,如果基類的屬性值發生變化,對應的子類引用的基類的屬性的值也相應發生了變化。
# 錯誤示例:
class A(object):
x = 1
class B(A):
pass
class C(A):
pass
>>> B.x = 2
>>> print(A.x, B.x, C.x)
1 2 1
>>> A.x = 3
>>> print(A.x, B.x, C.x)
3 2 3
1
2
3
4
5
6
7
8
9
10
11
12
13
這裏雖然沒有給C.x賦值,但是由于基類的值A.x發生改變,在獲取C.x的值得時候發現它引用的數據發生了變化。在上面這段代碼中,因爲屬性x沒有在類C中發現,它會查找它的基類(在上面例子中只有A,盡管Python支持多繼承)。換句話說,就是C自己沒有x屬性,因此,引用C.x其實就是引用A.x。
# 正确示例:
# 如果希望类`C`中的`x`不引用自`A`类,可以在`C`类中重新定义属性`X`,
# 这样`C`类的就不会引用`A`类的属性`x`了,值的变化就不会相互影响。
class B(A):
x = 2
class C(A):
x = -1
>>> print(A.x, B.x, C.x)
1 2 -1
>>> A.x = 3
>>> print(A.x, B.x, C.x)
3 2 -1
1
2
3
4
5
6
7
8
9
10
11
12
8.3 严禁使用注释行等形式仅使功能失效
说明:python的注释包含:单行注释、多行注释、代码间注释、doc string等。除了doc string是使用""""""括起来的多行注释,常用来描述类或者函数的用法、功能、参数、返回等信息外,其余形式注释都是使用#符号开头用来注释掉#后面的内容。基于python語言运行时编译的特殊性,如果在提供代码的时候提供的是py文件,即便是某些函数和方法在代码中进行了注释,别有用心的人依然可以通过修改注释来使某些功能启用;尤其是某些接口函数,如果不在代码中进行彻底删除,很可能在不知情的情况下就被启用了某些本应被屏蔽的功能。因此根据红线要求,在python中不使用的功能、模块、函数、变量等一定要在代码中彻底删除,不给安全留下隐患。即便是不提供源码py文件,提供编译过的pyc、pyo文件,别有用心的人可以通过反编译来获取源代码,可能会造成不可预测的结果。
# 錯誤示例:在 main.py 中有两个接口被注释掉了,但是没有被删除。
if __name__ == "__main__":
if sys.argv[1].startswith(‘--‘):
option = sys.argv[1][2:]
if option == "load":
#安裝應用
LoadCmd(option, sys.argv[2:3][0])
elif option == ‘unload‘:
#卸載應用
UnloadCmd(sys.argv[2:3][0])
elif option == ‘unloadproc‘:
#卸載流程
UnloadProcessCmd(sys.argv[2:3][0])
# elif option == ‘active‘:
# ActiveCmd(sys.argv[2:3][0])
# elif option == ‘inactive‘:
# InActiveCmd(sys.argv[2:3][0])
else:
Loginfo("Command %s is unknown"%(sys.argv[1]))
# 在上例中很容易让其他人看到我们程序中的两个屏蔽的接口,容易造成不安全的因素,注释的代码应该删除。
# 正确实例
if __name__ == "__main__":
if sys.argv[1].startswith(‘--‘):
option = sys.argv[1][2:]
if option == "load":
#安裝應用
LoadCmd(option, sys.argv[2:3][0])
elif option == ‘unload‘:
#卸載應用
UnloadCmd(sys.argv[2:3][0])
elif option == ‘unloadproc‘:
#卸載流程
UnloadProcessCmd(sys.argv[2:3][0])
else:
Loginfo("Command %s is unknown"%(sys.argv[1]))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
8.4 慎用copy和 deepcopy
說明:在python中,對象賦值實際上是對象的引用。當創建一個對象,然後把它賦給另一個變量的時候,python並沒有拷貝這個對象,而只是拷貝了這個對象的引用。如果需要拷貝對象,需要使用標准庫中的copy模塊。copy模塊提供copy和deepcopy兩個方法:
copy淺拷貝:拷貝一個對象,但是對象的屬性還是引用原來的。對于可變類型,比如列表和字典,只是複制其引用。基于引用所作的改變會影響到被引用對象。
deepcopy深拷貝:創建一個新的容器對象,包含原有對象元素(引用)全新拷貝的引用。外圍和內部元素都拷貝對象本身,而不是引用。
Notes:對于數字,字符串和其他原子類型對象等,沒有被拷貝的說法。如果對其重新賦值,也只是新創建一個對象,替換掉舊的而已。使用copy和deepcopy時,需要了解其使用場景,避免錯誤使用。
# 示例:
>>> import copy
>>> a = [1, 2, [‘x‘, ‘y‘]]
>>> b = a
>>> c = copy.copy(a)
>>> d = copy.deepcopy(a)
>>> a.append(3)
>>> a[2].append(‘z‘)
>>> a.append([‘x‘, ‘y‘])
>>> print(a)
[1, 2, [‘x‘, ‘y‘, ‘z‘], 3, [‘x‘, ‘y‘]]
>>> print(b)
[1, 2, [‘x‘, ‘y‘, ‘z‘], 3, [‘x‘, ‘y‘]]
>>> print(c)
[1, 2, [‘x‘, ‘y‘, ‘z‘]]
>>> print(d)
[1, 2, [‘x‘, ‘y‘]]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
8.5 系统路径推荐使用 pathlib.Path
使用pathlib.Path庫中的方法代替字符串拼接來完成文件系統路徑的操作
说明:pathlib.Path 基于 os.path库实现了一系列文件系统路径操作方法,这些方法相比单纯的路径字符串拼接来说更为安全,而且为用户屏蔽了不同操作系统之间的差异。
# 錯誤示例:如下路径字符串的拼接在Windows操作系统无法使用
path = os.getcwd() + ‘/myDirectory‘
# 正确示例:
path = pathlib.Path(os.getcwd(), ‘myDirectory‘)
path = pathlib.Path().resolve() / ‘myDirectory‘
1
2
3
4
5
6
8.6 使用subprocess模块代替os.system模块来执行shell命令
說明:subprocess模塊可以生成新進程,連接到它們的input/output/error管道,並獲取它們的返回代碼。該模塊旨在替換os.system等舊模塊,相比os.system模塊來說更爲靈活。
# 推荐做法:
>>> subprocess.run(["ls", "-l"]) # doesn‘t capture output
CompletedProcess(args=[‘ls‘, ‘-l‘], returncode=0)
>>> subprocess.run("exit 1", shell=True, check=True)
Traceback (most recent call last):
...
subprocess.CalledProcessError: Command ‘exit 1‘ returned non-zero exit status 1
>>> subprocess.run(["ls", "-l", "/dev/null"], capture_output=True)
CompletedProcess(args=[‘ls‘, ‘-l‘, ‘/dev/null‘], returncode=0,
stdout=b‘crw-rw-rw- 1 root root 1, 3 Jan 23 16:23 /dev/null\n‘, stderr=b‘‘)
1
2
3
4
5
6
7
8
9
10
8.7 建议使用with语句操作文件
说明:Python 对一些内建对象进行改进,加入了对上下文管理器的支持,可以用于 with 语句中。使用 with 语句可以自动关闭文件,减少文件读取操作错误的可能性,在代码量和健壮性上更优。注意 with 语句要求其操作的类型实现 __enter__() 和 __exit__() 方法,需确认实现后再使用。
# 推荐做法:
with open(r‘somefileName‘) as somefile:
for line in somefile:
print(line)
# ...more code
# 此使用with语句的代码等同于以下使用try...finally...结构的代码。
somefile = open(r‘somefileName‘)
try:
for line in somefile:
print(line)
# ...more code
finally:
somefile.close()
————————————————
版权声明:本文为CSDN博主「zhao12501」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文鏈接:https://blog.csdn.net/zhao12501/article/details/115473209
標簽:print info 語言 流程 system 拼接 管理 支持 __name__
原文地址:https://www.cnblogs.com/chengjian-physique/p/14995386.html