357

Is there any difference at all between both approaches?

>>> os.getenv('TERM')
'xterm'
>>> os.environ.get('TERM')
'xterm'

>>> os.getenv('FOOBAR', "not found") == "not found"
True
>>> os.environ.get('FOOBAR', "not found") == "not found"
True

They seem to have the exact same functionality.

ivanleoncz
  • 9,070
  • 7
  • 57
  • 49
André Staltz
  • 13,304
  • 9
  • 48
  • 58

5 Answers5

204

See this related thread. Basically, os.environ is found on import, and os.getenv is a wrapper to os.environ.get, at least in CPython.

EDIT: To respond to a comment, in CPython, os.getenv is basically a shortcut to os.environ.get ; since os.environ is loaded at import of os, and only then, the same holds for os.getenv.

Community
  • 1
  • 1
W. Conrad Walden
  • 2,242
  • 2
  • 13
  • 5
  • 3
    Indeed, it is, according to the official doc: https://docs.python.org/3/library/os.html – ivanleoncz Apr 18 '19 at 07:02
  • 4
    From the linked related thread: "the main reason to use `os.getenv()` [...] is when you want to have a default value returned when an environment variable name isn't found in `os.environ`'s keys rather than have a `KeyError` or whatever thrown, and you want to save a few characters." – mindthief Aug 11 '19 at 02:12
  • 13
    `os.environ.get` does not throw `KeyError`. – Sam Brightman Oct 29 '20 at 12:58
169

One difference (observed in Python 2.7 and 3.8) between getenv() and environ[]:

  • os.getenv() does not raise an exception, but returns None
  • os.environ.get() similarly returns None
  • os.environ[] raises an exception if the environmental variable does not exist
Mathieu CAROFF
  • 1,230
  • 13
  • 19
giwyni
  • 2,928
  • 1
  • 19
  • 11
  • 186
    The OP asks about `os.environ.get()` which returns `None` (unless specified differently) and never raises an exception if the env. var. doesn't exists. Your confusing things with using `os.environ['TERM']` which is not what the question is about. – Anthon Apr 21 '17 at 07:41
  • 11
    The OP's *question* asks about `os.environ.get()` vs `os.getenv()` but the *body* also includes `os.environ` vs. `os.environ.get()` so this answer correct in at least some ways - incomplete, but correct. – FKEinternet Jul 01 '19 at 18:12
  • 11
    This incorrect and misleading answer should have received negative down votes. The next answer is the right one. – RayLuo Apr 11 '20 at 00:33
  • Someone edited this answer to "fix" it and now there are a bunch of old downvotes and comments on it... It would have been better to just downvote it and post the fix in a new anwer. – alexia Nov 23 '21 at 14:51
  • 1
    @nyuszika7h - The older revisions were vague - they simple said `os.environ` would raise an exception if the variable didn't exist. They didn't clearly say `os.environ[]` which is what we're now praising as being correct, or `os.environ.get()` which is what I think some people were assuming was meant and criticizing as being wrong. The latest update clearly mentions both of them and accurately describes all of their behaviors. – ArtOfWarfare Feb 02 '22 at 16:16
62

While there is no functional difference between os.environ.get and os.getenv, there is a massive difference between os.putenv and setting entries on os.environ. os.putenv is broken, so you should default to os.environ.get simply to avoid the way os.getenv encourages you to use os.putenv for symmetry.

os.putenv changes the actual OS-level environment variables, but in a way that doesn't show up through os.getenv, os.environ, or any other stdlib way of inspecting environment variables:

>>> import os
>>> os.environ['asdf'] = 'fdsa'
>>> os.environ['asdf']
'fdsa'
>>> os.putenv('aaaa', 'bbbb')
>>> os.getenv('aaaa')
>>> os.environ.get('aaaa')

You'd probably have to make a ctypes call to the C-level getenv to see the real environment variables after calling os.putenv. (Launching a shell subprocess and asking it for its environment variables might work too, if you're very careful about escaping and --norc/--noprofile/anything else you need to do to avoid startup configuration, but it seems a lot harder to get right.)

user2357112
  • 260,549
  • 28
  • 431
  • 505
54

In Python 2.7 with iPython:

>>> import os
>>> os.getenv??
Signature: os.getenv(key, default=None)
Source:
def getenv(key, default=None):
    """Get an environment variable, return None if it doesn't exist.
    The optional second argument can specify an alternate default."""
    return environ.get(key, default)
File:      ~/venv/lib/python2.7/os.py
Type:      function

So we can conclude os.getenv is just a simple wrapper around os.environ.get.

Zulu
  • 8,765
  • 9
  • 49
  • 56
9

In addition to the answers above:

$ python3 -m timeit -s 'import os' 'os.environ.get("TERM_PROGRAM")'
200000 loops, best of 5: 1.65 usec per loop

$ python3 -m timeit -s 'import os' 'os.getenv("TERM_PROGRAM")'
200000 loops, best of 5: 1.83 usec per loop

EDIT: meaning, no difference

fredrik
  • 9,631
  • 16
  • 72
  • 132
  • Which version of Python have you tested with. On 3.7.2, `os.getenv` is just a wrapper for `os.environ.get`, so I am getting very minimal overhead. – Preslav Rachev Jan 07 '19 at 15:19
  • That was 3.7.1 on macOS Mojave. Timings were pretty consistent. – fredrik Jan 07 '19 at 16:51
  • 1
    @PreslavRachev minimal or not it is an extra function call, so there is some overhead. That being said, you probably don't need to retrieve env variables in the middle of your inner loop. – pmav99 Feb 24 '19 at 14:08
  • 32
    Totally irrelevant. Micro-optimization of a single function call... Any application should read the environment only on bootstrap anyways, making this even more irrelevant. – Victor Schröder Mar 26 '19 at 14:06
  • 7
    BTW, `usec` is a _microsecond_ in `timeit`. The difference found in this micro-benchmarking was 0.18 microseconds... – Victor Schröder Mar 26 '19 at 14:08