2

If I execute the following:

@dataclass
class Test:
    hi: Optional[str] = field(
        default_factory=lambda: str(uuid.uuid1()))

@dataclass
class Test2:
    hi: Optional[str] = str(uuid.uuid1())

if __name__ == "__main__":
    a = Test() # Test(hi='115a865c-33be-11eb-94a1-34c93db23648')
    b = Test() # Test(hi='115a865d-33be-11eb-8c6f-34c93db23648')
    c = Test2() # Test2(hi='10e44300-33be-11eb-85eb-34c93db23648')
    d = Test2() # Test2(hi='10e44300-33be-11eb-85eb-34c93db23648')

Why Test will return for every instance a different uuid, while Test2 will return the same each time?

Also, why default factory is needed and not default?

Chen
  • 73
  • 1
  • 8
  • Does this answer your question? [Passing default list argument to dataclasses](https://stackoverflow.com/questions/52063759/passing-default-list-argument-to-dataclasses) – Tom Wojcik Dec 01 '20 at 10:28
  • @TomWojcik No, I saw that but didn't understand how it answers my use case TBH – Chen Dec 01 '20 at 10:33

1 Answers1

6

As stated in the docs

default_factory: If provided, it must be a zero-argument callable that will be called when a default value is needed for this field. Among other purposes, this can be used to specify fields with mutable default values, as discussed below. It is an error to specify both default and default_factory.

In your first example

@dataclass
class Test:
    hi: Optional[str] = field(
        default_factory=lambda: str(uuid.uuid1()))

you pass an anonymous function to the default factory.

To make things easier, lets make a non anonymous function.

def get_new_uuid() -> str:
    return str(uuid.uuid1())


@dataclass
class Test:
    hi: Optional[uuid.UUID] = field(default_factory=get_new_uuid)

so each instance of Test calls get_new_uuid function to obtain new uuid, when initialized.


Your second example assigns a default value.

@dataclass
class Test2:
    hi: Optional[str] = str(uuid.uuid1())

It's an equivalent of this

@dataclass
class Test2:
    hi: Optional[str] = field(default=str(uuid.uuid1()))

As you can see, here default parameter is used, while previously it was default_factory. Here, you don't pass a function, but a value. It's a one-time thing.

In Test you can't access hi without instantiating it. But Test2 has default value assigned on import, so you can access it without instantiating. It's similar to class attribute and instance attribute defined in __init__.

Tom Wojcik
  • 5,471
  • 4
  • 32
  • 44
  • "In Test you can't access hi without instantiating it. But Test2 has default value assigned on import, so you can access it without instantiating. It's similar to class attribute and instance attribute defined in __init__." That's the difference I looked for. Thanks! – Chen Dec 01 '20 at 12:05