為什么我們需要if __name__ == __main__:

[目錄]
0.前言
1.什么是 `__name__`?
2.`if __name__ == '__main__'`: 的作用
3.為何Windows更需`if __name__ =`?

前言

if __name__ == '__main__': 是 Python 中一個非常重要的慣用法,尤其在使用 multiprocessing 模塊或編寫可導入的模塊時。它的作用是區分腳本是直接運行還是被導入,從而控制代碼的執行行為。很多初學者可能對此感到困惑,不明白其真正的用途和重要性。

直到發現自己的CPU因為不良的代碼習慣而干燒了數十分鐘才知道后悔

下面詳細解釋它的作用和工作原理。


1. 什么是 __name__

__name__ 是 Python 中的一個內置變量,它的值取決于腳本的運行方式:

  • 當腳本被直接運行時(例如通過 python script.py 運行):

    • __name__ 的值是 '__main__'
    • 這是 Python 解釋器自動設置的,表示當前腳本是“主腳本”。
  • 當腳本被導入為模塊時(例如 import script):

    • __name__ 的值是模塊的名稱(例如 script)。
    • 此時,腳本中的代碼會被執行,但 __name__ 不再是 '__main__'

示例

假設有一個腳本 example.py

print("The value of __name__ is:", __name__)if __name__ == '__main__':print("This script is being run directly.")
else:print("This script is being imported as a module.")
  • 直接運行

    python example.py
    

    輸出:

    The value of __name__ is: __main__
    This script is being run directly.
    
  • 作為模塊導入: 創建另一個腳本 importer.py

    import example
    

    沒錯,這個新腳本就這么短小精悍。

    輸出:

    The value of __name__ is: example
    This script is being imported as a module.
    

2.if __name__ == '__main__': 的作用

if __name__ == '__main__': 的作用是讓某些代碼塊只在腳本被直接運行時執行,而在腳本被導入時不執行。這有以下幾個重要用途:

避免導入時的副作用

當一個 Python 腳本被導入為模塊時,腳本中的所有頂層代碼(不在函數或類中的代碼)都會被執行。
如果這些頂層代碼包含不希望在導入時運行的操作(例如啟動服務器、執行復雜計算、創建進程等),會產生意外行為。
使用 if name == ‘main’:,可以確保這些代碼只在腳本被直接運行時執行。

示例

假設有一個腳本 math_utils.py:

# 頂層代碼
print("This will always run when the script is imported!")def add(a, b):return a + b# 不使用 if __name__ == '__main__':
result = add(2, 3)
print(f"Result of add(2, 3): {result}")

另一個腳本 main.py導入了math_utils:

import math_utilsprint("Using math_utils to add numbers...")
print(math_utils.add(5, 6))

運行main.py:

This will always run when the script is imported!
Result of add(2, 3): 5
Using math_utils to add numbers...
11

問題在于,math_utils.py 中的頂層代碼(printresult = add(2, 3))在導入時被執行了,這可能不是我們想要的。
現在使用 if __name__ == '__main__': 修改 math_utils.py:

print("This will always run when the script is imported!")def add(a, b):return a + bif __name__ == '__main__':result = add(2, 3)print(f"Result of add(2, 3): {result}")

這時我們得到的運行后結果為:

This will always run when the script is imported!
Using math_utils to add numbers...
11

我們可以發現,if __name__ == '__main__': 塊中的代碼(result = add(2, 3) 和相關的 print)只在 math_utils.py 被直接運行時執行,導入時不會運行。

其他用途

(1) 測試代碼

你可以在 if name == ‘main’: 中添加測試代碼,這些代碼只在腳本直接運行時執行,而不會在導入時運行。譬如:

def add(a, b):return a + bif __name__ == '__main__':# 測試代碼print(add(2, 3))print(add(5, 6))

直接運行時,測試代碼會執行;而導入時,測試代碼則不會運行。

(2) 命令行工具

許多命令行工具使用 if __name__ == '__main__': 來定義入口點,確保主邏輯只在直接運行時執行。譬如:

import sysdef main():print("Hello, world!")print("Arguments:", sys.argv)if __name__ == '__main__':main()

如果不使用 if __name__ == '__main__': 保護,主邏輯會在腳本被導入時意外執行,這可能導致不希望的行為,尤其是在命令行工具中。

3. 為何Windows更需if __name__ =

這主要取決于我們 coding \texttt{coding} coding的場景——是否會用到multiprocessing 或是其它類似方法來加速我們的計算。

在 Linux 上,multiprocessing 默認使用 fork 方法創建子進程。fork 會直接復制主進程的內存狀態,子進程不會重新加載腳本,因此頂層代碼不會被重復執行。

在 Windows 上,multiprocessing 使用 spawn 方法,必須重新加載腳本,導致頂層代碼被重復執行,因此需要 if __name__ == '__main__': 保護。

值得提醒的是,當我們在 Github \texttt{Github} Github上拿到其它大佬的項目代碼時,我們自己在本地運行時一定要檢查是否存在類似問題。以pix2pix與CycleGAN項目為例,其原始代碼為:

import os
import numpy as np
import cv2
import argparse
from multiprocessing import Pooldef image_write(path_A, path_B, path_AB):im_A = cv2.imread(path_A, 1) # python2: cv2.CV_LOAD_IMAGE_COLOR; python3: cv2.IMREAD_COLORim_B = cv2.imread(path_B, 1) # python2: cv2.CV_LOAD_IMAGE_COLOR; python3: cv2.IMREAD_COLORim_AB = np.concatenate([im_A, im_B], 1)cv2.imwrite(path_AB, im_AB)parser = argparse.ArgumentParser('create image pairs')
parser.add_argument('--fold_A', dest='fold_A', help='input directory for image A', type=str, default='../dataset/50kshoes_edges')
parser.add_argument('--fold_B', dest='fold_B', help='input directory for image B', type=str, default='../dataset/50kshoes_jpg')
parser.add_argument('--fold_AB', dest='fold_AB', help='output directory', type=str, default='../dataset/test_AB')
parser.add_argument('--num_imgs', dest='num_imgs', help='number of images', type=int, default=1000000)
parser.add_argument('--use_AB', dest='use_AB', help='if true: (0001_A, 0001_B) to (0001_AB)', action='store_true')
parser.add_argument('--no_multiprocessing', dest='no_multiprocessing', help='If used, chooses single CPU execution instead of parallel execution', action='store_true',default=False)
args = parser.parse_args()for arg in vars(args):print('[%s] = ' % arg, getattr(args, arg))splits = os.listdir(args.fold_A)if not args.no_multiprocessing:pool=Pool()for sp in splits:img_fold_A = os.path.join(args.fold_A, sp)img_fold_B = os.path.join(args.fold_B, sp)img_list = os.listdir(img_fold_A)if args.use_AB:img_list = [img_path for img_path in img_list if '_A.' in img_path]num_imgs = min(args.num_imgs, len(img_list))print('split = %s, use %d/%d images' % (sp, num_imgs, len(img_list)))img_fold_AB = os.path.join(args.fold_AB, sp)if not os.path.isdir(img_fold_AB):os.makedirs(img_fold_AB)print('split = %s, number of images = %d' % (sp, num_imgs))for n in range(num_imgs):name_A = img_list[n]path_A = os.path.join(img_fold_A, name_A)if args.use_AB:name_B = name_A.replace('_A.', '_B.')else:name_B = name_Apath_B = os.path.join(img_fold_B, name_B)if os.path.isfile(path_A) and os.path.isfile(path_B):name_AB = name_Aif args.use_AB:name_AB = name_AB.replace('_A.', '.')  # remove _Apath_AB = os.path.join(img_fold_AB, name_AB)if not args.no_multiprocessing:pool.apply_async(image_write, args=(path_A, path_B, path_AB))else:im_A = cv2.imread(path_A, 1) # python2: cv2.CV_LOAD_IMAGE_COLOR; python3: cv2.IMREAD_COLORim_B = cv2.imread(path_B, 1) # python2: cv2.CV_LOAD_IMAGE_COLOR; python3: cv2.IMREAD_COLORim_AB = np.concatenate([im_A, im_B], 1)cv2.imwrite(path_AB, im_AB)
if not args.no_multiprocessing:pool.close()pool.join()

即是顯然是在 Linux \texttt{Linux} Linux系統上使用的multiprocessing方法。本人一開始尚未注意到該問題,結果CPU干燒了十幾分鐘,出現類似的 RuntimeError \texttt{RuntimeError} RuntimeError

將代碼修正后:

import os
import numpy as np
import cv2
import argparse
from multiprocessing import Pooldef image_write(path_A, path_B, path_AB):im_A = cv2.imread(path_A, 1)im_B = cv2.imread(path_B, 1)if im_A is None or im_B is None:print(f"Failed to load images: {path_A} or {path_B}")returnim_AB = np.concatenate([im_A, im_B], 1)cv2.imwrite(path_AB, im_AB)if __name__ == '__main__':parser = argparse.ArgumentParser('create image pairs')parser.add_argument('--fold_A', dest='fold_A', help='input directory for image A', type=str, default='../dataset/50kshoes_edges')parser.add_argument('--fold_B', dest='fold_B', help='input directory for image B', type=str, default='../dataset/50kshoes_jpg')parser.add_argument('--fold_AB', dest='fold_AB', help='output directory', type=str, default='../dataset/test_AB')parser.add_argument('--num_imgs', dest='num_imgs', help='number of images', type=int, default=1000000)parser.add_argument('--use_AB', dest='use_AB', help='if true: (0001_A, 0001_B) to (0001_AB)', action='store_true')parser.add_argument('--no_multiprocessing', dest='no_multiprocessing', help='If used, chooses single CPU execution instead of parallel execution', action='store_true', default=False)args = parser.parse_args()for arg in vars(args):print('[%s] = ' % arg, getattr(args, arg))splits = os.listdir(args.fold_A)if not args.no_multiprocessing:pool = Pool()for sp in splits:img_fold_A = os.path.join(args.fold_A, sp)img_fold_B = os.path.join(args.fold_B, sp)img_list = os.listdir(img_fold_A)if args.use_AB:img_list = [img_path for img_path in img_list if '_A.' in img_path]num_imgs = min(args.num_imgs, len(img_list))print('split = %s, use %d/%d images' % (sp, num_imgs, len(img_list)))img_fold_AB = os.path.join(args.fold_AB, sp)if not os.path.isdir(img_fold_AB):os.makedirs(img_fold_AB)print('split = %s, number of images = %d' % (sp, num_imgs))for n in range(num_imgs):name_A = img_list[n]path_A = os.path.join(img_fold_A, name_A)if args.use_AB:name_B = name_A.replace('_A.', '_B.')else:name_B = name_Apath_B = os.path.join(img_fold_B, name_B)if os.path.isfile(path_A) and os.path.isfile(path_B):print(f"Found pair: {path_A} and {path_B}")name_AB = name_Aif args.use_AB:name_AB = name_AB.replace('_A.', '.')  # remove _Apath_AB = os.path.join(img_fold_AB, name_AB)if not args.no_multiprocessing:pool.apply_async(image_write, args=(path_A, path_B, path_AB))else:image_write(path_A, path_B, path_AB)else:print(f"Pair not found: {path_A} or {path_B}")if not args.no_multiprocessing:pool.close()pool.join()

終于能夠正常運行。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/78829.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/78829.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/78829.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

速盾:高防CDN的原理和高防IP一樣嗎?

隨著互聯網的發展,網絡安全威脅日益嚴重,尤其是DDoS攻擊、CC攻擊等惡意行為,給企業帶來了巨大的風險。為了應對這些挑戰,許多企業開始采用高防CDN(內容分發網絡)和高防IP作為防御措施。盡管兩者都能提供一定…

《算法筆記》3.6小節——入門模擬->字符串處理

1009 說反話 #include <cstdio>int main() {char sen[80][80];int num0;while(scanf("%s",sen[num])!EOF){num;}for (int i num-1; i > 0; --i) {printf("%s ",sen[i]);}printf("%s\n",sen[0]);return 0; }字符串連接 #include <io…

供應鏈業務-供應鏈全局觀(三)- 供應鏈三流的集成

概述 供應鏈的全局觀的全兩篇文章主要描述了供應鏈的基礎概念和供應鏈的協作和集成問題。 供應鏈業務-供應鏈全局觀&#xff08;一&#xff09;定義了什么是供應鏈和供應鏈管理。 所謂供應鏈就是把采購進來的東西&#xff0c;通過自身的生成加工&#xff0c;進行增值服務&am…

鏈表-算法小結

鏈表 單鏈表 雙鏈表 循環鏈表 鏈表_stl-CSDN博客 虛擬頭結點 反轉鏈表 刪除鏈表元素 方法一: 直接使用原來的鏈表來進行刪除操作。 頭節點是否為空頭鏈表的值是否為要刪除的值頭結點刪除后,新的頭節點是否依舊要刪除 ,刪除后的,新頭節點可能是空結點 方法二: 設置一個虛擬…

C語言中常用的調試宏和函數總結(__LINE__、__FUNCTION__)

表格&#xff1a;C語言調試工具 類別工具描述示例代碼預定義宏__LINE__表示當前源代碼的行號。printf("Error occurred at line %d\n", __LINE__);__FILE__表示當前源代碼文件的名稱。printf("Error occurred in file %s\n", __FILE__);__func__表示當前函…

DotnetCore開源庫SampleAdmin源碼編譯

1.報錯: System.Net.Sockets.SocketException HResult0x80004005 Message由于目標計算機積極拒絕&#xff0c;無法連接。 SourceSystem.Net.Sockets StackTrace: 在 System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, C…

如何使用切片操作來處理序列數據

1 問題 本文主要探究 Python 中切片操作的原理和應用。具體來說&#xff0c;我們將分析切片的基本語法、切片的步長和切片的邊界&#xff0c;并通過示例代碼展示如何使用切片操作來處理序列數據。 2 方法 為了更好地理解切片操作&#xff0c;我們采用如下的思路學習python中的切…

java(二):java的運算和流程控制

java中單引號和雙引號區別和用法 區別1&#xff1a;java中的單引號表示字符&#xff0c;雙引號表示字符串。 區別2&#xff1a;單引號引的數據一般是char類型的&#xff1b;雙引號引的數據 是String類型的。 區別3&#xff1a;java中單引號里面只能放一個字母或數字或符號&…

Android envsetup與Python venv使用指南

Android envsetup 和 Python venv 是兩種完全不同的環境配置工具&#xff0c;分別服務于不同的開發場景。以下是對它們的詳細解釋及使用方法&#xff1a; 1. Android envsetup 用途&#xff1a; Android envsetup 是 Android 源碼開發中的環境配置腳本&#xff08;envsetup.sh…

游戲引擎學習第222天

回顧昨天的過場動畫工作 我們正在制作一個游戲&#xff0c;目標是通過直播的方式完成整個游戲的開發。在昨天的工作中&#xff0c;我享受了制作過場動畫的過程&#xff0c;所以今天我決定繼續制作多個層次的過場動畫。 昨天我們已經開始了多層次過場動畫的基本制作&#xff0…

Leedcode刷題 | Day31_貪心算法05

一、學習任務 56. 合并區間代碼隨想錄738. 單調遞增的數字968. 監控二叉樹 二、具體題目 1.56合并區間56. 合并區間 - 力扣&#xff08;LeetCode&#xff09; 給出一個區間的集合&#xff0c;請合并所有重疊的區間。 示例 1: 輸入: intervals [[1,3],[2,6],[8,10],[15,1…

app逆向專題五:新快報app數據采集

app逆向專題五:新快報app數據采集 一、抓包尋找數據接口二、編寫代碼三、完整代碼一、抓包尋找數據接口 打開charles,并在手機端打開新快報app,點擊“廣州”或者“經濟”等選項卡,抓包,尋找數據接口,如圖所示: 二、編寫代碼 這里介紹一種簡便的代碼編寫方法,在數據…

Java面試黃金寶典45

1. 非對稱加密 RSA 定義:RSA 是一種廣泛使用的非對稱加密算法,其安全性基于大整數分解的困難性。它使用一對密鑰,即公鑰和私鑰。公鑰可公開用于加密消息,而私鑰必須保密,用于解密由相應公鑰加密的消息。要點: 公鑰公開,私鑰保密,二者成對出現。加密和解密使用不同的密鑰…

提權實戰!

就是提升權限&#xff0c;當我們拿到一個shell權限較低&#xff0c;當滿足MySQL提權的要求時&#xff0c;就可以進行這個提權。 MySQL數據庫提權&#xff08;Privilege Escalation&#xff09;是指攻擊者通過技術手段&#xff0c;從低權限的數據庫用戶提升到更高權限&#xff…

在虛擬機上修改saprk的版本

之前安裝的spark版本是3.4&#xff0c;現在實驗需要的版本是2.4。現在需要更改spark的版本。 方法很簡單&#xff1a; 直接將原有的spark3.4的文件刪除&#xff0c;再安裝2.4版本。 安裝過程之后再寫。Spark2.1.0入門&#xff1a;Spark的安裝和使用_廈大數據庫實驗室博客

文獻分享: DESSERT基于LSH的多向量檢索(Part3.2.外部聚合的聯合界)

原論文 文章目錄 1. \textbf{1. } 1. 定理 4.2 \textbf{4.2} 4.2的內容 1.1. \textbf{1.1. } 1.1. 一些符號 1.2. \textbf{1.2. } 1.2. 定理內容 3. \textbf{3. } 3. 聯合界限 Ps. \textbf{Ps. } Ps. 運行時間分析 1. \textbf{1. } 1. 定理 4.2 \textbf{4.2} 4.2的內容 1.1. \t…

MIPI協議介紹

MIPI協議介紹 mipi 協議分為 CSI 和DSI,兩者的區別在于 CSI用于接收sensor數據流 DSI用于連接顯示屏 csi分類 csi 分為 csi2 和 csi3 csi2根據物理層分為 c-phy 和 d-phy, csi-3采用的是m-phy 一般采用csi2 c-phy 和 d-phy的區別 d-phy的時鐘線和數據線是分開的,2根線一對…

【中間件】nginx反向代理實操

一、說明 nginx用于做反向代理&#xff0c;其目標是將瀏覽器中的請求進行轉發&#xff0c;應用場景如下&#xff1a; 說明&#xff1a; 1、用戶在瀏覽器中發送請求 2、nginx監聽到瀏覽器中的請求時&#xff0c;將該請求轉發到網關 3、網關再將請求轉發至對應服務 二、具體操作…

在3ds Max中視口顯示為黑色或深灰色

在3ds Max中視口顯示為黑色或深灰色 Autodesk Support 2023年10月8日 涵蓋的產品和版本 問題&#xff1a; 在3ds Max中&#xff0c;使用“深”UI方案時視口顯示為完全黑色&#xff0c;使用“淺”UI方案時視口顯示為深灰色。 原因&#xff1a; 已為用戶界面禁用Gamma校正。…

Vue.js 中 v-if 的使用及其原理

在 Vue.js 的開發過程中&#xff0c;條件渲染是一項極為常見的需求。v-if指令作為 Vue.js 實現條件渲染的關鍵手段&#xff0c;能夠根據表達式的真假來決定是否渲染某一塊 DOM 元素。它在優化頁面展示邏輯、提升用戶體驗等方面發揮著重要作用。接下來&#xff0c;我們就深入探討…