this post was submitted on 16 Mar 2026
1 points (100.0% liked)

Python

7870 readers
1 users here now

Welcome to the Python community on the programming.dev Lemmy instance!

πŸ“… Events

PastNovember 2023

October 2023

July 2023

August 2023

September 2023

🐍 Python project:
πŸ’“ Python Community:
✨ Python Ecosystem:
🌌 Fediverse
Communities
Projects
Feeds

founded 2 years ago
MODERATORS
 

For example:

class FooBar:
    def __init__(self):
        self.a: int = None
        self.foo: str = None

Is this bad practice/go against PEP guidelines or is it fine?

top 24 comments
sorted by: hot top controversial new old
[–] jtrek@startrek.website 0 points 4 weeks ago (1 children)

That seems like that's going to give you an error in most type checkers. You said it's always an int and then immediate made that a lie and made it None instead.

Why are you trying to do this?

[–] AstroLightz@lemmy.world 0 points 4 weeks ago (1 children)

I'm initializing variables that would be used later in the class in different functions. I wasn't sure if I needed to do a var: <type> | None = None or if just setting it to None was fine.

[–] solrize@lemmy.ml 0 points 4 weeks ago* (last edited 4 weeks ago) (1 children)

I'm going to say initializing them to None and updating them later is a code smell. If you can do so, make them non-optional and always initialize them to actual meaningful values.

[–] jtrek@startrek.website 0 points 4 weeks ago (1 children)

Yeah, if you initialize them to None then for the entire rest of the class you're going to have to account for the possibility that they're None. If it's unavoidable that they might be None, you should type it as such.

If you type them as like str | None then later when you do like return foo[0] it will warn you that you can't do that with None.

[–] solrize@lemmy.ml 0 points 4 weeks ago

foo[0] is also unsafe unless foo is known to be nonempty, ofc.

[–] sbv@sh.itjust.works 0 points 4 weeks ago (1 children)

Why not require values in the constructor?

[–] AstroLightz@lemmy.world 0 points 4 weeks ago

This is an example. For my actual use case, they would be private vars that would be set by class functions instead of passed to the constructor.

[–] joyjoy@lemmy.zip 0 points 4 weeks ago (1 children)

Implicit optional is deprecated. Explicitly use self.foo: int | None = None

[–] AstroLightz@lemmy.world 0 points 4 weeks ago

Thanks! Good to know.

[–] Vulwsztyn@programming.dev 0 points 3 weeks ago* (last edited 3 weeks ago)

yes, bad practice; yes, against PEP

[–] solrize@lemmy.ml 0 points 4 weeks ago (1 children)

If it can be None then the type is Optional[int] or Optional[str] as the case may be. Or I guess now int | None etc. I'm used to older versions of mypy so idk if they support that alternate syntax.

Optional[int] would seem to be preferable but it turns out it's not really an option type. Like Optional[None] being None is ambiguous.

[–] logging_strict@programming.dev 0 points 3 weeks ago* (last edited 3 weeks ago) (1 children)

kudos for pointing that out. Another reason to dump Optional.

Union[None] is not ambiguous

As coders we have the potential to have lots of coder friends. Union[None] is just the sad reality.

Optional[None] is sorta keeping our hopes up like there is some distant future where the situation improves. Where all these coder fiends come over every night of the week to bitch about static typing issues and running out of beer and pizza. And when that one guy has to bring up politics; everyone will react like he just let out a massive fart in the room, poisoning the air. Suddenly can feel the IQ in the room dramatically drop. Everyone reacts predicatively, spontaneously, and synchronously in disgust; thinking, dude what a tard! And someone will say, lets do this again tomorrow.

Fck yeah! This is the place where i want to live.

[–] solrize@lemmy.ml 0 points 3 weeks ago (1 children)

Not sure what you're getting at about Union but it's not an option type either. Option means a value might be present or absent. Like maybe there's a database column that holds an int, but that column is nullable so it has type Optional[int]. Now you might want to read a row from the db and get that column from it, but that row itself might be absent. That is, the value you return has type Optional[Optional[int]]. But if you get back None, does that mean the row was present and the column was nulled, or that the row was absent?

With a genuine option type you'd return something like Just None (Haskell notation) for the case where the row is present and the column is nulled. C++, Rust, ML, and so on all have versions of this that work. Python's version is sadly broken.

[–] logging_strict@programming.dev 0 points 3 weeks ago (1 children)

You are describing Sentinel. Recommend using attrs.NOTHING. Unfortunately, the static typing, NothingType, is in private API. Until the Python community can settle on the one Sentinel to rule them all, will stick with attrs cuz it's usually already a dependency.

You can come by my place anytime for beers and pizza. Bitching about static typing should be an Olympic sport. Or at least involve beer.

[–] solrize@lemmy.ml 0 points 3 weeks ago (1 children)

You might like: https://curtispoe.org/articles/what-to-know-before-debating-type-systems.html

I remember looking at attrs but not getting around to using it. These days I use dataclasses. I don't worry about this stuff too much in Python. I just notice some issues and smile. Haskell does a lot more to focus the mind, when it comes to types.

[–] logging_strict@programming.dev 0 points 3 weeks ago (1 children)

i'm in love with typing_extensions. Know why? I'm not on the latest greatest Python version. But want to use the usable latest features. As package author/maintainer, upgrading to the latest and greatest is not a viable option.

in py39, dataclasses lacked critical features that came out later. Especially for a fundamental structure like a dataclass, new features requiring a Python upgrade is a show stopper. On the level of a flaw. Either backport or in a separate package or i don't want to touch it. That's why use attrs and not dataclasses.dataclass

And so should we all.

There is always going to be some great features we want to use and then not being able to is not great UX. Every package that matters really must be external, except for Python features like free threading, sub-interpretors, etc.

There are always many coding languages, unfortunately jumping from one to the next is a pipe dream. So really doesn't matter what Haskell can do. Or how Rust devs all have 10 gfs.

[–] solrize@lemmy.ml 0 points 3 weeks ago* (last edited 3 weeks ago) (1 children)

Before dataclasses I used namedtuple and that was good enough. attrs looked like an improvement over namedtuple but not enough of one to deal with the added complexity. If dataclasses have gotten interesting new features since 3.9 I haven't seen them yet. I guess I should check the docs. I'm not a fancy user so it's probably ok.

Haskell is worth learning mostly because of how it sharpens your mind. Developing useful programs in it still has obstacles.

[–] logging_strict@programming.dev 0 points 3 weeks ago (1 children)

dataclasses required py310+ for critical features like slot support. Only now that py39 is history, is dataclasses even a thing. But then a must have feature will require an even more recent python version. So stuck waiting in a never ending loop.

Fck that!

python -m pip install attrs

Problem solved.

[–] solrize@lemmy.ml 0 points 3 weeks ago (1 children)

I haven't needed slot support in dataclasses so far. It must not be that critical. Some of my machines still have python 3.9 and dataclasses work, at least to the extent I've needed them to.

[–] logging_strict@programming.dev 0 points 3 weeks ago (1 children)

That's fine. But you coulda had access to latest greatest updates. Whether you need them or not.

[–] solrize@lemmy.ml 0 points 3 weeks ago (1 children)

Python advertises itself as being mature, which means changes if any are supposed to be incremental and backwards compatible. It's not so successful at that but I still don't particularly try to keep up with the bleeding edge. I don't know what slots in dataclasses even means. I do like the match statement.

[–] logging_strict@programming.dev 0 points 3 weeks ago (1 children)

LOL have never used the match statement. Has been one of those features just outta reach. So close can taste it. But alas ....

Now py310 is here. Still not enthusiastic about it. But over time maybe will come around.

__slots__ = (...) or frozen. Same thing. Make the data class read-only reducing memory usage by 30-40%. Like the memory usage difference between tuple and list.

From experience, in stubs, __slots__ need to include "__weakref__". If frozen, then don't include "__weakref__".

How about this. Lets meet each other half way.

I'll stop thinking of the match statement as thorium reactor toxic waste.

And maybe you can take a read through of the frozen option for dataclasses.

Sound fair?

[–] solrize@lemmy.ml 0 points 3 weeks ago

If I need something like slots I'll keep them in mind. Thanks for calling my attention to them.

[–] logging_strict@programming.dev 0 points 3 weeks ago* (last edited 3 weeks ago)

As can surmise from my handle, i'm even stricter!

Use Union, not | or Optional where ever possible.

Optional does not allow for future expansion of the typing. Optional[int] and then want to add float, so have to rewrite it Union[int, float, None] So write it with Union to begin with.

Went thru my | stage like everyone else, but grew out of it.

TypeAlias insists on Union, not |

So Union everywhere ftw

Why? If there is a possibility of making a mistake, Murphy's law dictates that it'll occur every damn time. Enough already! KISS principle is in effect.