<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Python中配置環(huán)境變量的幾種姿勢

          共 9712字,需瀏覽 20分鐘

           ·

          2021-03-17 13:36


          大家好,歡迎來到 Crossin的編程教室 !

          在運行一個項目的時候,我們經(jīng)常會遇到設置不同環(huán)境的需求,如設置是開發(fā)環(huán)境、測試環(huán)境還是生產(chǎn)環(huán)境,或者在某些設置里面可能還需要設置一些變量開關(guān),如設置調(diào)試開關(guān)、日志開關(guān)、功能開關(guān)等等。

          這些變量其實就是在項目運行時我們給項目設置的一些參數(shù)。這些參數(shù)一般情況來說,可以有兩種設置方法,一種是通過命令行參數(shù),一種是通過環(huán)境變量。二者的適用范圍不同,在不同的場景下我們可以選用更方便的方式來實現(xiàn)參數(shù)的設置。

          本節(jié)我們以 Python 項目為例,說說環(huán)境變量的設置。

          設置和獲取環(huán)境變量

          首先,我們先來了解一下在 Python 項目里面怎樣設置和獲取變量。

          首先讓我們定義一個最簡單的 Python 文件,命名為 main.py,內(nèi)容如下:

          import os
          print(os.environ['VAR1'])

          在這里我們導入了 os 模塊,它的 environ 對象里面就包含了當前運行狀態(tài)下的所有環(huán)境變量,它其實是一個os._Environ對象,我們可以通過類似字典取值的方式從中獲取里面包含的環(huán)境變量的值,如代碼所示。

          好,接下來我們什么也不設置,直接運行,看下結(jié)果:

          python3 main.py

          結(jié)果如下:

          raise KeyError(key) from None
          KeyError: 'VAR1'

          直接拋出來了一個錯誤,這很正常,我們此時并沒有設置 VAR1 這個環(huán)境變量,當然會拋出鍵值異常的錯誤了。

          接下來我們在命令行下進行設置,運行如下命令:

          VAR1=germey python3 main.py

          運行結(jié)果如下:

          germey

          可以看到我們在運行之前,在命令行之前通過鍵值對的形式對環(huán)境變量進行設置,程序就可以獲取到 VAR1 這個值了,成功打印出來了 germey。

          但這個環(huán)境變量是永久的嗎?我們這次再運行一遍原來的命令:

          python3 main.py

          結(jié)果如下:

          raise KeyError(key) from None
          KeyError: 'VAR1'

          嗯,又拋錯了。

          這說明了什么,在命令行的前面加上的這個環(huán)境變量聲明只能對當前執(zhí)行的命令生效。

          好,那既然如此,我難道每次運行都要在命令行前面加上這些聲明嗎?那豈不麻煩死了。

          當然有解決方法,我們使用 export 就可以了。

          比如這里,我們執(zhí)行如下命令:

          export VAR1=germey

          執(zhí)行完這個命令之后,當前運行環(huán)境下 VAR1 就被設置成功了,下面我們運行的命令都能獲取到 VAR1 這個環(huán)境變量了。

          下面來試試,還是執(zhí)行原來的命令:

          python3 main.py

          結(jié)果如下:

          germey

          可以,成功獲取到了 VAR1 這個變量,后面我們運行的每一個命令就都會生效了。

          但等一下,這個用了 export 就是永久生效了嗎?

          其實并不是,其實這個 export 只對當前的命令行運行環(huán)境生效,我們只要把命令行關(guān)掉再重新打開,之前用 export 設置的環(huán)境變量就都沒有了。

          可以試試,重新打開命令行,再次執(zhí)行原來的命令,就會又拋出鍵值異常的錯誤了。

          那又有同學會問了,我要在每次命令行運行時都想自動設置好環(huán)境變量怎么辦呢?

          這個就更好辦了,只需要把 export 的這些命令加入到~/.bashrc文件里面就好了,每次打開命令行的時候,系統(tǒng)都會自動先執(zhí)行以下這個腳本里面的命令,這樣環(huán)境變量就設置成功了。當然這里面還有很多不同的文件,如~/.bash_profile~/.zshrc、~/.profile/etc/profile等等,其加載是有先后順序的,大家感興趣可以去了解下。

          好了,扯遠了,我們現(xiàn)在已經(jīng)了解了如何設置環(huán)境變量和基本的環(huán)境變量獲取方法了。


          更安全的獲取方式

          但是上面的這種獲取變量的方式實際上是非常不友好的,萬一這個環(huán)境變量沒設置好,那豈不是就報錯了,這是很不安全的。

          所以,下面再介紹幾種比較友好的獲取環(huán)境變量的方式,即使沒有設置過,也不會報錯。

          我們可以把中括號取值的方式改成 get 方法,如下所示:

          import os
          print(os.environ.get('VAR1'))

          這樣就不會報錯了,如果 VAR1 沒設置,會直接返回 None,而不是直接報錯。

          另外我們也可以給 get 方法傳入第二個參數(shù),表示默認值,如下所示:

          import os
          print(os.environ.get('VAR1', 'germey'))

          這樣即使我們?nèi)绻O置過 VAR1,他就會用 germey 這個字符串代替,這就完成了默認環(huán)境變量的設置。

          下面還有幾種獲取環(huán)境變量的方式,總結(jié)如下:

          import os
          print(os.getenv('VAR1', 'germey'))

          這個方式比上面的寫法更簡單,功能完全一致。

          弊端

          但其實上面的方法有一個不方便的地方,如果我們想要設置非字符串類型的環(huán)境變量怎么辦呢?比如設置 int 類型、float 類型、list 類型,可能我們的寫法就會變成這個樣子:

          import os
          import json

          VAR1 = int(os.getenv('VAR1', 1))
          VAR2 = float(os.getenv('VAR2', 5.5))
          VAR3 = json.loads(os.getenv('VAR3'))

          然后設置環(huán)境變量的時候就變成這樣子:

          export VAR1=1
          export VAR2=2.3
          export VAR3='["1", "2"]'

          這樣才能成功獲取到結(jié)果,打印出來結(jié)果如下:

          1
          2.3
          ['1', '2']

          不過看下這個,寫法也太奇葩了吧,又是類型轉(zhuǎn)換,又是 json 解析什么的,有沒有更好的方法來設置。

          environs

          當然有的,下面推薦一個 environs 庫,利用它我們可以輕松地設置各種類型的環(huán)境變量。

          這是一個第三方庫,可以通過 pip 來安裝:

          pip3 install environs

          好,安裝之后,我們再來體驗一下使用 environs 來設置環(huán)境變量的方式。

          from environs import Env

          env = Env()
          VAR1 = env.int('VAR1', 1)
          VAR2 = env.float('VAR2', 5.5)
          VAR3 = env.list('VAR3')

          這里 environs 直接提供了 int、float、list 等方法,我們就不用再去進行類型轉(zhuǎn)換了。

          與此同時,設置環(huán)境變量的方式也有所變化:

          export VAR1=1
          export VAR2=2.3
          export VAR3=1,2

          這里 VAR3 是列表,我們可以直接用逗號分隔開來。

          打印結(jié)果如下:

          1
          2.3
          ['1', '2']

          官方示例

          下面我們再看一個官方示例,這里示例了一些常見的用法。

          首先我們來定義一些環(huán)境變量,如下:

          export GITHUB_USER=sloria
          export MAX_CONNECTIONS=100
          export SHIP_DATE='1984-06-25'
          export TTL=42
          export ENABLE_LOGIN=true
          export GITHUB_REPOS=webargs,konch,ped
          export COORDINATES=23.3,50.0
          export LOG_LEVEL=DEBUG

          這里有字符串、有日期、有日志級別、有字符串列表、有浮點數(shù)列表、有布爾。

          我們來看下怎么獲取,寫法如下:

          from environs import Env

          env = Env()
          env.read_env() # read .env file, if it exists
          # required variables
          gh_user = env("GITHUB_USER") # => 'sloria'
          secret = env("SECRET") # => raises error if not set

          # casting
          max_connections = env.int("MAX_CONNECTIONS") # => 100
          ship_date = env.date("SHIP_DATE") # => datetime.date(1984, 6, 25)
          ttl = env.timedelta("TTL") # => datetime.timedelta(0, 42)
          log_level = env.log_level("LOG_LEVEL") # => logging.DEBUG

          # providing a default value
          enable_login = env.bool("ENABLE_LOGIN", False) # => True
          enable_feature_x = env.bool("ENABLE_FEATURE_X", False) # => False

          # parsing lists
          gh_repos = env.list("GITHUB_REPOS") # => ['webargs', 'konch', 'ped']
          coords = env.list("COORDINATES", subcast=float) # => [23.3, 50.0]

          通過觀察代碼可以發(fā)現(xiàn)它提供了這些功能:

          • 通過 env 可以設置必需定義的變量,如果沒有定義,則會報錯。
          • 通過 date、timedelta 方法可以對日期或時間進行轉(zhuǎn)化,轉(zhuǎn)成 datetime.date 或 timedelta 類型。
          • 通過 log_level 方法可以對日志級別進行轉(zhuǎn)化,轉(zhuǎn)成 logging 里的日志級別定義。
          • 通過 bool 方法可以對布爾類型變量進行轉(zhuǎn)化。
          • 通過 list 方法可以對逗號分隔的內(nèi)容進行 list 轉(zhuǎn)化,并可以通過 subcast 方法對 list 的每個元素進行類型轉(zhuǎn)化。

          可以說有了這些方法,定義各種類型的變量都不再是問題了。

          支持類型

          總的來說,environs 支持的轉(zhuǎn)化類型有這么多:

          • env.str
          • env.bool
          • env.int
          • env.float
          • env.decimal
          • env.list(accepts optionalsubcastkeyword argument)
          • env.dict(accepts optionalsubcastkeyword argument)
          • env.json
          • env.datetime
          • env.date
          • env.timedelta(assumes value is an integer in seconds)
          • env.url
          • env.uuid
          • env.log_level
          • env.path(casts to apathlib.Path)

          這里 list、dict、json、date、url、uuid、path 個人認為都還是比較有用的,另外 list、dict 方法還有一個 subcast 方法可以對元素內(nèi)容進行轉(zhuǎn)化。

          對于 dict、url、date、uuid、path 這里我們來補充說明一下。

          下面我們定義這些類型的環(huán)境變量:

          export VAR_DICT=name=germey,age=25
          export VAR_JSON='{"name": "germey", "age": 25}'
          export VAR_URL=https://cuiqingcai.com
          export VAR_UUID=762c8d53-5860-4d5d-81bc-210bf2663d0e
          export VAR_PATH=/var/py/env

          需要注意的是,DICT 的解析,需要傳入的是逗號分隔的鍵值對,JSON 的解析是需要傳入序列化的字符串。

          解析寫法如下:

          from environs import Env

          env = Env()
          VAR_DICT = env.dict('VAR_DICT')
          print(type(VAR_DICT), VAR_DICT)

          VAR_JSON = env.json('VAR_JSON')
          print(type(VAR_JSON), VAR_JSON)

          VAR_URL = env.url('VAR_URL')
          print(type(VAR_URL), VAR_URL)

          VAR_UUID = env.uuid('VAR_UUID')
          print(type(VAR_UUID), VAR_UUID)

          VAR_PATH = env.path('VAR_PATH')
          print(type(VAR_PATH), VAR_PATH)

          運行結(jié)果如下:

          <class 'dict'> {'name': 'germey', 'age': '25'}
          <class 'dict'> {'name': 'germey', 'age': 25}
          <class 'urllib.parse.ParseResult'> ParseResult(scheme='https', netloc='cuiqingcai.com', path='', params='', query='', fragment='')
          <class 'uuid.UUID'> 762c8d53-5860-4d5d-81bc-210bf2663d0e
          <class 'pathlib.PosixPath'> /var/py/env

          可以看到,它分別給我們轉(zhuǎn)化成了 dict、dict、ParseResult、UUID、PosixPath 類型了。

          在代碼中直接使用即可。

          文件讀取

          如果我們的一些環(huán)境變量是定義在文件中的,environs 還可以進行讀取和加載,默認會讀取本地當前運行目錄下的.env文件。

          示例如下:

          from environs import Env

          env = Env()
          env.read_env()
          APP_DEBUG = env.bool('APP_DEBUG')
          APP_ENV = env.str('APP_ENV')
          print(APP_DEBUG)
          print(APP_ENV)

          下面我們在.env文件中寫入如下內(nèi)容:

          APP_DEBUG=false
          APP_ENV=prod

          運行結(jié)果如下:

          False
          prod

          沒問題,成功讀取。

          當然我們也可以自定義讀取的文件,如.env.test文件,內(nèi)容如下:

          APP_DEBUG=false
          APP_ENV=test

          代碼則可以這么定義:

          from environs import Env

          env = Env()
          env.read_env(path='.env.test')
          APP_DEBUG = env.bool('APP_DEBUG')
          APP_ENV = env.str('APP_ENV')

          這里就通過 path 傳入了定義環(huán)境變量的文件路徑即可。

          前綴處理

          environs 還支持前綴處理,一般來說我們定義一些環(huán)境變量,如數(shù)據(jù)庫的連接,可能有 host、port、password 等,但在定義環(huán)境變量的時候往往會加上對應的前綴,如 MYSQL_HOST、MYSQL_PORT、MYSQL_PASSWORD 等,但在解析時,我們可以根據(jù)前綴進行分組處理,見下面的示例:

          # export MYAPP_HOST=lolcathost
          # export MYAPP_PORT=3000

          with env.prefixed("MYAPP_"):
          host = env("HOST", "localhost") # => 'lolcathost'
          port = env.int("PORT", 5000) # => 3000

          # nested prefixes are also supported:

          # export MYAPP_DB_HOST=lolcathost
          # export MYAPP_DB_PORT=10101

          with env.prefixed("MYAPP_"):
          with env.prefixed("DB_"):
          db_host = env("HOST", "lolcathost")
          db_port = env.int("PORT", 10101)

          可以看到這里通過 with 和 priefixed 方法組合使用即可實現(xiàn)分區(qū)處理,這樣在每個分組下再賦值到一個字典里面即可。

          合法性驗證

          有些環(huán)境變量的傳入是不可預知的,如果傳入一些非法的環(huán)境變量很可能導致一些難以預料的問題。比如說一些可執(zhí)行的命令,通過環(huán)境變量傳進來,如果是危險命令,那么會非常危險。

          所以在某些情況下我們需要驗證傳入的環(huán)境變量的有效性,看下面的例子:

          # export TTL=-2
          # export NODE_ENV='invalid'
          # export EMAIL='^_^'

          from environs import Env
          from marshmallow.validate import OneOf, Length, Email

          env = Env()

          # simple validator
          env.int("TTL", validate=lambda n: n > 0)
          # => Environment variable "TTL" invalid: ['Invalid value.']


          # using marshmallow validators
          env.str(
          "NODE_ENV",
          validate=OneOf(
          ["production", "development"], error="NODE_ENV must be one of: {choices}"
          ),
          )
          # => Environment variable "NODE_ENV" invalid: ['NODE_ENV must be one of: production, development']

          # multiple validators
          env.str("EMAIL", validate=[Length(min=4), Email()])
          # => Environment variable "EMAIL" invalid: ['Shorter than minimum length 4.', 'Not a valid email address.']

          在這里,我們通過 validate 方法,并傳入一些判斷條件。如 NODE_ENV 只允許傳入 production 和 develpment 其中之一;EMAIL 必須符合 email 的格式。

          這里依賴于 marshmallow 這個庫,里面有很多驗證條件,大家可以了解下。

          如果不符合條件的,會直接拋錯,例如:

          marshmallow.exceptions.ValidationError: ['Invalid value.']

          關(guān)于 marshmallow 庫的用法,大家可以參考:https://marshmallow.readthedocs.io/en/stable/,后面我也抽空寫一下介紹下。

          最后再附一點我平時定義環(huán)境變量的一些常見寫法,如:

          import platform
          from os.path import dirname, abspath, join
          from environs import Env
          from loguru import logger

          env = Env()
          env.read_env()

          # definition of flags
          IS_WINDOWS = platform.system().lower() == 'windows'

          # definition of dirs
          ROOT_DIR = dirname(dirname(abspath(__file__)))
          LOG_DIR = join(ROOT_DIR, env.str('LOG_DIR', 'logs'))

          # definition of environments
          DEV_MODE, TEST_MODE, PROD_MODE = 'dev', 'test', 'prod'
          APP_ENV = env.str('APP_ENV', DEV_MODE).lower()
          APP_DEBUG = env.bool('APP_DEBUG', True if APP_ENV == DEV_MODE else False)
          APP_DEV = IS_DEV = APP_ENV == DEV_MODE
          APP_PROD = IS_PROD = APP_DEV == PROD_MODE
          APP_TEST = IS_TEST = APP_ENV = TEST_MODE

          # redis host
          REDIS_HOST = env.str('REDIS_HOST', '127.0.0.1')
          # redis port
          REDIS_PORT = env.int('REDIS_PORT', 6379)
          # redis password, if no password, set it to None
          REDIS_PASSWORD = env.str('REDIS_PASSWORD', None)
          # redis connection string, like redis://[password]@host:port or rediss://[password]@host:port
          REDIS_CONNECTION_STRING = env.str('REDIS_CONNECTION_STRING', None)

          # definition of api
          API_HOST = env.str('API_HOST', '0.0.0.0')
          API_PORT = env.int('API_PORT', 5555)
          API_THREADED = env.bool('API_THREADED', True)

          # definition of flags
          ENABLE_TESTER = env.bool('ENABLE_TESTER', True)
          ENABLE_GETTER = env.bool('ENABLE_GETTER', True)
          ENABLE_SERVER = env.bool('ENABLE_SERVER', True)

          # logger
          logger.add(env.str('LOG_RUNTIME_FILE', 'runtime.log'), level='DEBUG', rotation='1 week', retention='20 days')
          logger.add(env.str('LOG_ERROR_FILE', 'error.log'), level='ERROR', rotation='1 week')

          這里定義了一些開發(fā)環(huán)境、日志路徑、數(shù)據(jù)庫連接、API 設置、開關(guān)設置等等,是從我之前寫的一個代理池項目拿來的,大家可以參考:https://github.com/Python3WebSpider/ProxyPool。

          好了,以上就是一些環(huán)境變量的定義方法。

          如果文章對你有幫助,歡迎轉(zhuǎn)發(fā)/點贊/收藏~

          作者:崔慶才
          來源:進擊的Coder


          _往期文章推薦_

          Python中常見的配置文件寫法




          如需了解付費精品課程教學答疑服務
          請在Crossin的編程教室內(nèi)回復: 666

          瀏覽 40
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲成人福利导航 | 在线观看的A片 | 日本一级片在线看 | 欧美亚洲日韩性爱 | 91视频18 |