importiofromcontextlibimportredirect_stdout# Easy redirect sys.stdout to another file or file-like objectf=io.StringIO()withredirect_stdout(f):importthisf.getvalue()# You could easily redirect stdout to a file:withopen('import_this.txt','w')asf:withredirect_stdout(f):importthis# You could also make a custom StringIO with a complex behaviorclassCaptureStdOutput(io.StringIO):"""Captures IO stream and emit a signal."""defwrite(self,text):"""Do something awesome instead of writing to std."""passd=CaptureStdOutput()withredirect_stdout(d):importthisd.getvalue()# In python2 you should overwrite sys.stdout# and pull it back after redirecting the outputimportsysstdout=sys.stdoutsys.stdout=open('import_this.txt','w')importthissys.stdout=stdout

fromcontextlibimportExitStackwithExitStack()asstack:files=[stack.enter_context(open(fname))forfnamein['/etc/hosts','nosuchfile']]# All opened files will automatically be closed at the end of# the with statement, even if attempts to open files later# in the list raise an exception

# super() no longer requires passing the class name and selfclassA:def__init__(self,arg):self.arg=argclassB(A):def__init__(self):super().__init__("hello")# in python 2 this would be:#super(B, self).__init__("hello)b=B()print(b.arg)# hello

# underscores can be used to make numbers easier to read in codebig_number=100_000_000_000print(big_number)# 100000000000

# easy string formatting# no more '%{name}s' % {'name': name}name='Alice'print(f'My name is {name}.')# prints "My name is Alice"

# add type annotations to variables, arguments and functions# you can later access those programmatically# you can also use mypy to check for type errors# http://mypy-lang.org/defmy_add(a:int,b:int)->int:returna+bprint(my_add(40,2))# 42print(my_add.__annotations__)# {'a': <class 'int'>, 'b': <class 'int'>, 'return': <class 'int'>}

# easy way to use iterators in your generatordefweird_range():yieldfromrange(5)yieldfromrange(5)foriinweird_range():print(i)# in python 2 you had to manually iterate over the iteratordefpython2_weird_range():foriinrange(5):yieldiforiinrange(5):yieldi

# avoid accidental passing of keyword argumentsa,b,*rest=range(10)deff(a,b,*args,option=True):print(option)# in python 2 this would have passed option=False, but not in python 3f(a,b,False)# in both python 2 and 3 this explictly passes option=Falsef(a,b,option=False)

# built-in ipaddress module that helps you easily deal with IP addresses# check type, compare, check against subnets and more with both IPv4 and IPv6fromipaddressimportip_address,ip_networkip4=ip_address('192.168.0.1')print('packed',ip4.packed)print('is private',ip4.is_private)ip6=ip_address('2001:db8::')print('exploded',ip6.exploded)print('is multicast',ip6.is_multicast)net=ip_network('192.168.0.0/24')print(f'{ip4} in {net} =',ip4innet)print(f'{ip6} in {net} =',ip6innet)