class Node:
def __init__(self, edges = set()):
self.edges = edges
def main():
foo = Node()
bar = Node()
quz = Node()
foo.edges.add(bar)
bar.edges.add(foo)
assert(foo is not bar) # assertion succeeds
assert(foo is not quz) # assertion succeeds
assert(bar is not quz) # assertion succeeds
assert(len(quz.edges) == 0) # assertion fails??
main()
spoiler
Mutable default values are shared across objects. The set in this case.
Oh I had a similar bug but with defaulted dicts. Default args are constructed once and reused. Not a problem for immutable args, but mutables like dicts (and sets I’d also assume) are all shared.
EDIT: whoops, didn’t see you spoilered the answer, my bad! If it helps, i found my bug when dealing with cross-thread stuff, so that was a fun moment to bisect
You may like
collections.defaultdict
. Pass the constructor a factory function to be run when a key is missing.dd = defaultdict(list) dd['key'].append("value") print(dd['key']) # ["value"]
Ah sorry I meant a default argument which was a dict, thanks for the tip tho!
Yeah Pylint catches this. If you aren’t using Pylint you are writing bad Python.
That’s a funny way to spell Ruff
Yeah, I discovered this when a coworker wrote code like
def foo(timestamp = now())
and had fun debugging why there were a bunch of duplicate timestamps.PEP 671 would add new syntax to ease the pain, but it’s not accepted yet. It would allow for writing function definitions like one of these:
def bisect_right(a, x, lo=0, hi=>len(a), *, key=None): def connect(timeout=>default_timeout): def add_item(item, target=>[]): def format_time(fmt, time_t=>time.time()):