Effective Python学习笔记 - ch01 Using Expressions and Statements

1. Slice sequences

somelist [start:end] 

  • start:  inclusive index
  • end: exclusive index
  • works with built-in types: list, tuple, str, bytes
  • works with any class that defines __getitem__

For example:

  • Basic example: 
>> mylist = ['a','b','c','d','e','f','g','h']
>> print(mylist[:]) 
#Out: the whole list ['a','b','c','d','e','f','g','h']
>> print(mylist[-4:]) 
#Out: ['e','f','g','h']
>> print(mylist[3:-3]) 
#Out: ['d','e']
>> print(mylist[:-1]) 
#Out: ['a','b','c','d','e','f','g']
  • Noticed: the slice is the deep copy, changing the slice won't affect the original one
>> mylist_slice = mylist[4:]
>> print(mylist_slice) 
#Out: ['d','e','f','g','h']
>> #changing mylist_slice won't affect mylist
>> mylist_slice[2] = '99'
>>print(mylist_slice) 
#Out: ['d','e','99','g','h']
>>print(mylist) 
#Out: ['a','b','c','d','e','f','g','h']
>>#demonstrate slice is a deep copy: their addresses are different
mylist_copy = mylist[:]
print(id(mylist))       
#Out: 4501936776
print(id(mylist_copy))  
#Out: 4502919176

2. Avoid using start, end, and stride in a single slice

somelist[start:end:stride]

  • start: inclusive index
  • end: exclusive index
  • stride: index interval

For example:

>> a = ["red","blue","yellow","pink","white","black"]
>> print(a[::4])
#Out: ['red', 'white']
>> print(a[1::4])
#Out: ['blue', 'black']
>> y = a[::-1]
>> y
#Out: ['black', 'white', 'pink', 'yellow', 'blue', 'red']
>> print(a[-2::-2])
#Out: ['white', 'yellow', 'red']

3. Prefer Enumerate over Range

range(start,stop[,step]):

  • start: inclusive point, the begining point; default is 0
  • stop: excusive point
  • step: default is 1

enumerate(sequence, [start=0]): create tuples containing the index and list item using a list

  • sequence
  • start: the start of the index

Example 1: 

from random import randint
random_bits = 0
for i in range(64):
    if randint(0,1): #generate 0 or 1
        #If 1 is generated, setting the corresponding bit with 1
        random_bits |= 1 << i      
print(bin(random_bits))
#Output: 0b1111111101010010010110000100111001000110100101100010000110101100

Example 2:

mylist = ['a','b','c','d']
for item in mylist:
    print(item)
#Output:
#a
#b
#c
#d

for i in range(len(mylist)):
    item = mylist[i]
    print("%d: %s" %(i+1,item))
#Output:    
#1: a
#2: b
#3: c
#4: d

print(list(enumerate(mylist)))
#Output:
#[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd')]

for i,item in enumerate(mylist,start=1):
    print("%d: %s" %(i,item))

#Output:
#1: a
#2: b
#3: c
#4: d

4. Use zip to process iterators in parallel

zip (*iterables): 

  • iterables: can be built-in iterables, like list,string,dict, or user-defined iterables (object that has __iter__ method)

zip_longest(*iterables):

  • Similar with zip(), but will keep the longest items

For example:

  • Example 1: basic example
names = ["paul",'william','bob']
letters = [len(n) for n in names]
print(letters)
#Output: [4, 7, 3]

#Find the longest name
longest_name = None
max_letters = 0
for (name,letter) in zip(names,letters):
    if letter > max_letters:
        longest_name,max_letters = name,letter
print(longest_name,max_letters)
#Output: william 7

print(list(zip(names,letters)))
#Output: [('paul', 4), ('william', 7), ('bob', 3)]
  • Example 2: when different number of elements in iterables passed to zip(), zip_longest()
names = ["paul",'william','bob']
letters = [len(n) for n in names]
names.append("Rosalind")

for name,count in zip(names,letters):
    print("%s has %d letters" %(name,count))
#Output
#paul has 4 letters
#william has 7 letters
#bob has 3 letters


from itertools import zip_longest
for name,count in zip_longest(names,letters):
    if count is None:
        print("%s has is of unknown length" % name)
    else:
        print("%s has %d letters" %(name,count))
#Output
#paul has 4 letters
#william has 7 letters
#bob has 3 letters
#Rosalind has is of unknown length

print(list(zip_longest(names,letters)))
#Output: [('paul', 4), ('william', 7), ('bob', 3), ('Rosalind', None)]

5. Avoid else blocks after for and while loops

  •  The "ELSE" block after the for/while loop will always run except that the loop is ended by a "break" out.
    • If the loop is ended by a "break" out, then "else"  block won't run
    • If the loop is not ended by a "break" out, then "else" block will always run

It is confusing to use else block following the loop so that the author recommends us not use like that. The followings are some examples of "Else" block after the loop. 

for i in range(3):
    print("Loop %d" %i)
else:
    print("Else block")
    
#Output:
#Loop 0
#Loop 1
#Loop 2
#Else block


for i in range(3):
    print("Loop %d" %i)
    if i==1:
        break
else:
    print("Else block")

#Output:
#Loop 0
#Loop 1


for i in []:
    print("Never run")
else:
    print("Else block")
    
#Output:
#Else block

while False:
    print("never run")
else:
    print("Else block")

#output:
#Else block


a = 5
b = 9
for i in range(2, min(a,b)+1):
    print("testing",i)
    if a % i == 0 and b % i == 0:
        print("Not coprime")
        break
else:
    print("Coprime")
    
#Output:
#testing 2
#testing 3
#testing 4
#testing 5
#Coprime

  • "ElSE" block after in try/execept 
try:
    x=5
except:
    print("Caught exception")
else:
    print("everything is fine")

#Output: everything is fine

ry:
    raise Exception("Doh")
except:
    print("Caught exception")
finally:
    print("This always runs")

#output:
#Caught exception
#This always runs

 

6. Take advantage of each block in TRY/EXCEPT/ELSE/FINALLY

The template of try/except/else/finally: always remember the annotations below.

try:
    #Do something
except MyException as e:
    #Handle exception
else:
    #Runs when there are no exceptions
finally:
    #Always runs after try

Try to use "try, except,else and finally" together to make it clear what you are doing and what you are not doing at each step. To illustrate it, the author gives us a list of examples:

Example v1: open a file without any exception handles

handle = open("/tmp/hello.txt",encoding='utf-8')
handle.write("success\nand\nnew\nlines")
handle.close()

#Raise IOError if file doesn't exist

Example v2: compare two types of exception handles. Noticed: "else" block will run when there's no exception; "finally" block will always run successfully even there is an exception.

#v2 -1 
handle = open("/tmp/hello.txt",encoding='utf-8') #May Raise IOError
try:
    data = handle.read()  #May Raise UnicodeDecodeError
finally:
    handle.close()        #will always run


#v3 -2
try:
    handle = open("/tmp/hello.txt", encoding='utf-8')  # Raise IOError
    data = handle.read()  #May Raise UnicodeDecodeError
finally:
    handle.close()        
    #May also raise another exception if file doesn't exist, 
    #that will break the rule of "finally always runs"

Example v3: A complete example of try/except/else/finally

import json
UNDEFINED = object()
def divide_json(path):
    handle = open(path,'r+')    #IOError
    try:
        data = handle.read()    #UnicodeDecodeError
        op = json.loads(data)   #ValueError
        value = op['numerator'] / op['denominator'] #ZeroDivisionError
    except ZeroDivisionError:
        return UNDEFINED
    else:
        op['result'] = value
        result = json.dumps(op)
        handle.seek(0)
        handle.write(result)    #IOError
        return value
    finally:
        handle.close()          #Always runs


######## Test #################
temp_path = '/tmp/random_data.json'
with open(temp_path,'w') as handle:
    handle.write('{"numerator":1,"denominator":10}')
print(divide_json(temp_path))
#Output: 0.1

with open(temp_path,'w') as handle:
    handle.write('{"numerator":1,"denominator":0}')
print(divide_json(temp_path) is UNDEFINED)
#Output: True

with open(temp_path,'w') as handle:
    handle.write('{"numerator":hello world}')
print(divide_json(temp_path))
#Output: Traceback ....
#Raise JSONDecodeError

7. Consider CONTEXLIB and with statements for reusable TRY/FINALLY behavior

7.1 with makes it possible to factor out simplified standard uses of try/finally statements,for example: 

#Example: try/finally
f = open('file.txt', 'w')
try:
    f.write("Hello")
finally:
    f.close()

The previous example can be shortened by with statement like follows: 

#Example: the previous example can be shortened by with statement
with open("file.text","w") as f:
    f.write("Hello")

7.2 How does with statement works

In fact, with statement makes use of the context manager. 

7.2.1 What is a context manager 

A context manager is an object that defines the runtime context to be established when executing a with statementThe context manager handles the entry into, and the exist from, the desired runtime context for the execution of the block of code. Context managers are normally invoked using the with statement, but can also be used by directly invoking their methods.

 A context manager is an object that implements __enter__() and __exit__() functions. These two functions defines the entry and exist of the context respectively. 

7.2.2.  How with statement works. 

The following code is a general use format of with statement, that is the same as the second code block. 

with EXPRESSION as VAR:
    CODE BLOCK
contextManager = EXPRESSION
VAR = contextManager.__enter__()
try:
    CODE BLOCK
finally:
    contextManager.__exit__()

7.2.3. how to define a customized context manager.

Noticed that a context manger is a class that implements __enter__() and __exit__() methods. Let's take the File as an example: 

class File:
    def __init__(self,filename,mode):
        self.filename = filename
        self.mode = mode
    def __enter__(self):
        print("Enter")
        self.f = open(self.filename, self.mode)
        return self.f

    def __exit__(self, exc_type=None, exc_val=None, exc_tbs=None):
        print("Exist")
        self.f.close()

Now, the File is a context manger. And we can validate the theory given in the 7.2.2.

Firstly, let's run the following code:

with File('file.txt', 'w') as f:
    print("Writing...")
    f.write('Hello')

#Output:
#Enter
#Writing...
#Exist

 Then, we can use try/finally to implement a similar function. This function will get the same output as the previous example, that demonstrate the theory of with statement.

contextManager = File('file.txt', 'w')
VAR = contextManager .__enter__()
try:
    print("Writing...")
    VAR.write('Hello')
finally:
    contextManager.__exit__()

7.2.4.  contextmanager decorator in Python

In python, a contextmanager decorator is given to help users define a factory function for with statement context managers, without needing to create a class or separate __enter__() and __exit__() methods.

An abstract example is given in the following. In managed_resource(), the code ahead of finally corresponds to the __enter__(), the code in the finally corresponds to the __exit__() method. 

from contextlib import contextmanager
@contextmanager
def managed_resource(*args, **kwds):
    # Code to acquire resource, e.g.:
    resource = acquire_resource(*args, **kwds)
    try:
        yield resource
    finally:
        # Code to release resource, e.g.:
        release_resource(resource)

>>> with managed_resource(timeout=3600) as resource:
     # Resource is released at the end of this block,
     # even if code in the block raises an exception
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值