问题
How can I improve the speed of my algorithm, even by the slightest amount. For the reason I made this I don't actually care about speed in the slightest, but I'm wondering what I could change if I did. Even more than pointing out specific things to change, what I really want to know is how you know that there's a more efficient way and how that way works. Basically, what is the track to follow if you want to learn how the low level stuff works so that you can write better code at the higher level.
def decode_all(ob):
if(type(ob)==list):
for i in range(len(ob)):
ob[i] = decode_all(ob[i])
if(type(ob)==dict):
for i in ob.copy():
ob[i] = decode_all(ob[i])
if(type(i)==bytes):
ob[i.decode()] = ob[i]
ob.pop(i)
if(type(ob)==tuple):
new_ob = []
for i in ob:
new_ob.append(decode_all(i))
ob = tuple(new_ob)
if(type(ob)==bytes):
return ob.decode()
return ob
回答1:
Modified code:
def decode_all(ob):
if isinstance(ob, list):
return [decode_all(e) for e in ob]
if isinstance(ob, dict):
return {decode_all(k): decode_all(v) for k, v in ob.items()}
if isinstance(ob, tuple):
return tuple([decode_all(e) for e in ob])
if isinstance(ob, bytes):
return ob.decode()
return ob
Usually, I agree with the use of if ... elif ... elif ...
, but in this case, it's simpler to just return the result right away.
Some notes:
- No side effects: nothing in
ob
or any of its components (at whatever depth) is modified; all new collections and elements are returned, at some (slight) performance cost for their allocation. - Use
isinstance()
, notif type(x) == y
(just a good habit, no particular advantage here with base types). - Use list and dict comprehensions; far simpler, no fear of changing the original, and essentially pythonic.
- No need to handle the case where dict keys would be bytes: use your own function instead.
tuple([x for x in list_])
is slightly faster thantuple(x for x in list_)
, I learned recently.
Some alternatives:
- If you prefer
if ... elif ...
:
def decode_all(ob):
if isinstance(ob, list):
ob = [decode_all(e) for e in ob]
elif isinstance(ob, dict):
ob = {decode_all(k): decode_all(v) for k, v in ob.items()}
elif isinstance(ob, tuple):
ob = tuple(decode_all(e) for e in ob)
elif isinstance(ob, bytes):
ob = ob.decode()
return ob
- Fastest (but a bit opaque):
def _fun_bytes(ob):
return ob.decode()
def _fun_list(ob):
return [decode_all(e) for e in ob]
def _fun_dict(ob):
return {decode_all(k): decode_all(v) for k, v in ob.items()}
def _fun_tuple(ob):
return tuple([decode_all(e) for e in ob])
def _identity(ob):
return ob
fun_dict = {
bytes: _fun_bytes,
list: _fun_list,
dict: _fun_dict,
tuple: _fun_tuple,
}
def decode_all(ob):
return fun_dict.get(type(ob), _identity)(ob)
- Most extensible (you can add various
.register
in various areas in your codebase, e.g. to handle new types):
from functools import singledispatch
@singledispatch
def decode_all(ob):
return ob
@decode_all.register
def _(ob: bytes):
return ob.decode()
@decode_all.register
def _(ob: list):
return [decode_all(e) for e in ob]
@decode_all.register
def _(ob: dict):
return {decode_all(k): decode_all(v) for k, v in ob.items()}
@decode_all.register
def _(ob: tuple):
return tuple([decode_all(e) for e in ob])
来源:https://stackoverflow.com/questions/65555973/how-could-i-improve-the-speed-of-my-algorithm-even-by-the-slightest-amount