On Youtube, Raymond Hettinger (python core developper) explains common mistakes and correct writing in python Video I found? v = OSGv2VnC0go), so I will summarize it. In the video, I mainly use python2 as an example (because it is a 2013 video), but here I converted it to python3 as much as possible. Now that it may already be written in an old way, please use it while checking it as appropriate.
Loop
--Use iterator as much as possible
Bad example
for i in [0, 1, 2, 3, 4, 5]:
print(i**2)
Put the entire list in memory. ↓
Good example
for i in range(6):
print(i**2)
Since range
is generated one by one as an iterator, memory is not wasted.
In python2,
range
is a list andxrange
is an itertor In python3,range
is iterator (name ofxrange
in python2 has changed)
Bad example
colors = ['red', 'green', 'blue', 'yellow']
for i in range(len(colors)):
print(colors[i])
↓
Good example
colors = ['red', 'green', 'blue', 'yellow']
for color in colors:
print(color)
Writing below is faster
Bad example
colors = ['red', 'green', 'blue', 'yellow']
for i in range(len(colors)-1, -1, -1):
print(colors[i])
↓
Good example
colors = ['red', 'green', 'blue', 'yellow']
for color in reversed(colors):
print(color)
Bad example
colors = ['red', 'green', 'blue', 'yellow']
for i in range(len(colors)):
print(i, '--->', colors[i])
↓
Good example
colors = ['red', 'green', 'blue', 'yellow']
for i, color in enumerate(colors):
print(i, '--->', color)
Bad example
names = ['raymond', 'rachel', 'matthew']
colors = ['red', 'green', 'blue', 'yellow']
n = min(len(names), len(colors))
for i in range(n):
print(names[i], '--->'. colors[i]
↓
Good example
names = ['raymond', 'rachel', 'matthew']
colors = ['red', 'green', 'blue', 'yellow']
for name, color in zip(names, colors):
print(name, '--->', color)
When iterators of different lengths are entered in the zip, they will be aligned to the shorter one. ʻItertools.zip_longest` to align to the longer one
In python2,
zip
produces a list (ʻizipis an iterator), In python3
zip` generates iterator
Bad example
colors = ['red', 'green', 'blue', 'yellow']
def compare_length(c1, c2):
if len(c1) < len(c2):
return -1
elif len(c1) > len(c2):
return 1
else:
return 0
print(sorted(colors, cmp=compare_length)
↓
Good example
colors = ['red', 'green', 'blue', 'yellow']
print(sorted(colors, key=len))
** Is sorting by key sufficient? ** ** In some cases it is not enough, but in most cases it is okay. (SQL does a lot of sorting, but sorts by key)
Bad example
blocks = []
while True:
block = f.read(32)
if block == '':
break
blocks.append(block)
↓
Good example
blocks = []
for block in iter(functool.partial(f.read, 32), ''):
blocks.append(block)
partial is unpleasant, but the merit of being able to handle it as an iterator is great It is better to avoid sentinel value
Bad example
def find(seq, target):
found = False
for i, value in enumerate(seq):
if value == target:
found = True
break
if not found:
return -1
return i
Example when you have to use a flag (found
)
↓
Good example
def find(seq, target):
for i, value in enumerate(seq):
if value == target:
break
else:
return -1
return i
If there is no break in for, the else statement is executed. He regrets that he should have named it
nobreak
instead of else.
Dictionary
d = {'matthew': 'blue', 'rachel': 'green', 'raymond': 'red'}
for k in d:
print(k)
↑ Something strange happens when you make changes to the dictionary
d = {'matthew': 'blue', 'rachel': 'green', 'raymond': 'red'}
for k in d.keys():
if k.startswith('r'):
del d[k]
↑ d.keys ()
makes a copy of the list in advance, so you can change the dictionary
Bad example
d = {'matthew': 'blue', 'rachel': 'green', 'raymond': 'red'}
for k in d:
print(k, '--->', d[k])
↓
Good example
d = {'matthew': 'blue', 'rachel': 'green', 'raymond': 'red'}
for k, v in d.items():
print(k, '--->', v)
names = ['raymond', 'rachel', 'matthew']
colors = ['red', 'green', 'blue']
d = dict(zip(names, colors))
Inefficient method
colors = ['red', 'green', 'red', 'blue', 'green', 'red']
d = {}
for color in colors:
if color in d:
d[color] = 0
d[color] += 1
↓
The right way
d = {}
for color in colors:
d[color] = d.get(color, 0) + 1
↓
Recent method
d = defaultdict(int)
for color in colors:
d[color] += 1
Bad example
names = ['raymond', 'raychel', 'matthew', 'roger', 'betty', 'melisa', 'judith', 'charlie']
d = {}
for name in names:
key = len(name)
if key not in d:
d[key] = []
d[key].append(name)
↓
The right way
d = {}
for name in names:
key = len(name)
d.setdefault(key, []).append(name)
↓
Recent method
d = defaultdict(list)
for name in names:
key = len(name)
d[key].append(name)
get
does not assign to the dictionary.setdefault
substitutes
Bad example
defaults = {'color': 'red', 'user': 'guest'}
parser = argparse.ArgumentParser()
parser.add_argument('-u', '--user')
parser.add_argument('-c', '--color')
namespace = parser.parse_args([])
command_line_args = {k: v for k, v in vars(namespace).items() if v}
d = defaults.copy()
d.update(os.environ)
d.update(command_line_args)
A large number of dictionaries are copied ↓
Good example
d = collections.ChainMap(command_line_args, os.environ, defaults)
Keep the original dictionary as it is without copying
Bad example
twitter_search('@obama', False, 20, True)
I don't understand the meaning of the argument ↓
Good example
twitter_search('@obama', retweets=False, numtweets=20, popular=True)
NamedTuple
Bad example
> doctest.testmod()
(0, 4)
I don't understand the meaning of 0,4 ↓
Good example
> doctest.testmod()
TestResults(failed=0, attempted=4)
TestResults
is
TestResults = namedtuple('TestResults', ['failed', 'attempted'])
Can be made with
Bad example
p = 'Raymond', 'Hettinger', 0x30, '[email protected]'
fname = p[0]
lname = p[1]
age = p[2]
email = p[3]
↓
Good example
fname, lname, age, email = p
Bad example
def fibonacci(n):
x = 0
y = 1
for i in range(n):
print(x)
t = y
y = x + y
x = t
There is a moment when the state collapses during execution. Easy to get the line order wrong ↓
Good example
def fibonacci(n):
x, y = 0, 1
for i in range(n):
print(x)
x, y = y, x+y
This is closer to human thinking.
Bad example
names = ['raymond', 'raychel', 'matthew', 'roger', 'betty', 'melisa', 'judith', 'charlie']
s = names[0]
for name in names[1:]:
s += ', ' + name
↓
Good example
', '.join(names)
Bad example
names = ['raymond', 'raychel', 'matthew', 'roger', 'betty', 'melisa', 'judith', 'charlie']
del names[0]
names.pop(0)
names.insert(0, 'mark')
slow ↓
Good example
names = deque(['raymond', 'raychel', 'matthew', 'roger', 'betty', 'melisa', 'judith', 'charlie'])
del names[0]
names.popleft()
names.appendleft('mark')
fast
--Separate business logic and administrative logic --The code is clean ――If you don't name it correctly, it will be a mess.
Bad example
def web_lookup(url, saved={}):
if url in saved:
return saved[url]
page = urlib.urlopen(url).read()
saved[url] = page
return page
↓
Good example
@lru_cache()
def web_lookup(url):
return urllib.urlopen(url).read()
Business logic and administrative logic are separated
Bad example
oldcontext = getcontext().copy()
getcontext().prec = 50
print(Decimal(355) / Decimal(113))
setcontext(oldcontext)
↓
Good example
with localcontext(Context(prec=50)):
print(Decimal(355) / Decimal(113))
Bad example
f = open('data.txt')
try:
data = f.read()
finally:
f.close()
↓
Good example
with open('data.txt') as f:
data = f.read()
Bad example
lock = threading.Lock()
lock.acquire()
try:
print('Critical section 1')
print('Critical section 2')
finally:
lock.release()
↓
Good example
lock = threading.Lock()
with lock:
print('Critical section 1')
print('Critical section 2')
Bad example
try:
os.remove('somefile.tmp')
except OSError:
pass
↓
Good example
with ignored(OSError):
os.remove('somefile.tmp')
Bad example
with open('help.txt', 'w') as f:
oldstdout = sys.stdout
sys.stdout = f
try:
help(pow)
finally:
sys.stdout = oldstdout
↓
Good example
with open('help.txt', 'w') as f:
with redirect_stdout(f):
help(pow)
Bad example
result = []
for i in range(10):
s = i**2
result.append(a)
print(sum(result))
↓
Good example
print(sum([i**2 for i in range(10)])
↓
Good example
print(sum(i**2 for i in range(10)))
Recommended Posts