Skip to content

Latest commit

 

History

History
93 lines (69 loc) · 1.79 KB

README.md

File metadata and controls

93 lines (69 loc) · 1.79 KB

unique_defaults

Guarantee new objects are used for each function call.

This allows writing signatures with mutable defaults without sharing the underlying object between function calls.

from unique_defaults import unique_lists

def classic(a=[]):
    a.append("again")
    return " ".join(a)

classic() == "again"
classic() == "again again"

@unique_lists
def unique(a=[]):
    a.append("again")
    return " ".join(a)

unique() == "again"
unique() == "again"

Using the mutable object directly in the function signature leads to shorter and more accurate annotations, as well as simplifying the function's logic.

from typing import List, Optional
from unique_defaults import unique_lists

def classic(a: Optional[List] = None):
    if a is None:
        a = []
    a.append("again")
    return " ".join(a)

classic() == "again"
classic() == "again"

@unique_lists
def unique(a: List = []):
    a.append("again")
    return " ".join(a)

unique() == "again"
unique() == "again"

Individual arguments can be targeted, instead of a general type.

from unique_defaults import unique_defaults

@unique_defaults('line_list')
def log_list(line_list=[], _running_log=[]):
    line_list[:0] = ["NOTICE:"]
    line = " ".join(line_list)
    _running_log.append(line)
    return _running_log

log_list(["first"]) == ["NOTICE: first"]
log_list(["second"]) == ["NOTICE: first", "NOTICE: second"]

Any types can be made unique

from unique_defaults import unique_defaults

class Count:
    def __init__(self):
        self.count = 0
    def add(self, n):
        self.count += n

def classic(a=Count()):
    a.add(1)
    return a

classic().count == 1
classic().count == 2

@unique_defaults(Count)
def unique(a=Count()):
    a.add(1)
    return a

unique().count == 1
unique().count == 1