That’s why we use type-hinting at my company:
def do_work(foo: list | None): if not foo: return ...
Boom, self-documenting, faster, and very simple.
Comment on Python Performance: Why 'if not list' is 2x Faster Than Using len()
iAvicenna@lemmy.world 3 days ago
Yea and then you use “not” with a variable name that does not make it obvious that it is a list and another person who reads the code thinks it is a bool. Hell a couple of months later you yourself wont even understand that it is a list. You should not sacrifice code readability for over optimization, this is phyton after all I don’t think list lengths will be your bottle neck.
That’s why we use type-hinting at my company:
def do_work(foo: list | None): if not foo: return ...
Boom, self-documenting, faster, and very simple.
Well, in your case it is not clear whether you intended to branch in the variable foo being None, or on the list being empty which is semantically very different…
Thats why it’s better to explicitelly express whether you want an empty collection (len = 0) or a None value.
Well yeah, because I’m explicitly not defining a difference between None
and []
. In most cases, the difference doesn’t matter.
If I did want to differentiate, I’d use another if
block:
if foo is None: ... if not foo: ...
Explicit is better than implicit. I hate relying on exceptions like len(foo) == 0
raising a TypeError
because that’s very much not explicit.
Exceptions should be for exceptional cases, as in, things that aren’t expected. If it is expected, make an explicit check for it.
I don’t really understand the point about exceptions. Yeah “not foo” cannot throw an exception. But the program should crash if an invalid input is provided. If the function expects an optional[list] it should be provided with either a list or None, nothing else.
if you’re worried about readability you can leave a comment.
Comments shouldn’t explain code. Code should explain code by being readable.
Comments are for whys. Why is the code doing the things it’s doing. Why is the code doing this strange thing here. Why does a thing need to be in this order. Why do I need to store this value here.
Stuff like that.
There is no guarantee that the comment is kept up to date with the code. “Self documenting code” is a meme, but clearly written code is pretty much always preferable to unclear code with a comment, largely because you can actually be sure that the code does what it says it does.
Note: You still need to comment your code kids.
Better yet, a type hint. list | None
can be checked by static analysis, # foo is a list
isn’t.
If there is an alternative through which I can achieve the same intended effect and is a bit more safer (because it will verify that it has len implemented) I would prefer that to commenting. Also if I have to comment every len use of not that sounds quite redundant as len checks are very common
I really dislike using boolean operators on anything that is not a boolean. I recently made an esception to my rule and got punished… Yeah it is skill issue on my part that I tried to check that a variable equal to 0 was not None using “if variable…”. But many programming rules are there to avoid bugs caused by this kind of inattention.
In my experience, if you didn’t write the function that creates the list, there’s a solid chance it could be None
too, and if you try to check the length of None
, you get an error. This is also why returning None
when a function fails is bad practice IMO, but that doesn’t seem to stop my coworkers.
Passing None to a function expecting a list is the error…
good point I try to initialize None collections to empty collections in the beginning but not guaranteed and len would catch it
Sometimes there’s an important difference between None
and []
. That’s by far not the most common use, but it does exist (e.g. None
could mean “user didn’t supply any data” and []
could mean “user explicitly supplied empty data”).
If the distinction matters, make it explicit:
if foo is None: raise ValueError("foo must be defined for this operation") if not foo: return None for bar in foo: ... return some_other_value
This way you’re explicit about what constitutes an error vs no data, and the caller can differentiate as well. In most cases though, you don’t need that first check, if not foo
can probably just return None
or use some default value or whatever, and whether it’s None
or []
doesn’t matter.
if len(foo) == 0:
is bad for a few reasons:
TypeError
will be raised if it’s None
, which is probably unexpectedIf you don’t care about the distinction, handle both the same way. If you do care, handle them separately.
jerkface@lemmy.ca 3 days ago
Strongly disagree that
not x
implies to programmers thatx
is a bool.taladar@sh.itjust.works 3 days ago
It does if you are used to sane languages instead of the implicit conversion nonsense C and the “dynamic” languages are doing
iAvicenna@lemmy.world 3 days ago
well it does not imply directly per se since you can “not” many things but I feel like my first assumption would be it is used in a bool context
thebestaquaman@lemmy.world 3 days ago
I would say it depends heavily on the language. In Python, it’s very common that different objects have some kind of Boolean interpretation, so assuming that an object is a bool because it is used in a Boolean context is a bit silly.
iAvicenna@lemmy.world 3 days ago
Well fair enough but I still like the fact that len makes the aim and the object more transparent on a quick look through the code which is what I am trying to get at. The supporting argument on bools wasn’t’t very to the point I agree.
That being said is there an application of “not” on other classes which cannot be replaced by some other more transparent operator (I confess I only know the bool and length context)? I would rather have transparently named operators rather than having to remember what “not” does on ten different types. I like duck typing as much as the next guy, but when it is so opaque as in the case of not, I prefer alternatives. For instance having open or read on different objects which does really read or open some data vs not some object god knows what it does I should memorise each case.
Glitchvid@lemmy.world 3 days ago
if not x then … end
is very common in Lua for similar purposes, very rarely do you see hard nil comparisons or calls totypeof
(last time I did was for a serializer).catloaf@lemm.ee 3 days ago
You can make that assumption at your own peril.
WhyJiffie@sh.itjust.works 3 days ago
I don’t think they are a minority
iAvicenna@lemmy.world 3 days ago
If anything len tells you that it is a sequence or a collection, “not” does not tell you that. That I feel like is the main point of my objection.
jerkface@lemmy.ca 3 days ago
The main thing
not
is for is coercing a truthy value into an actual bool.acosmichippo@lemmy.world 3 days ago
i haven’t programmed since college 15 years ago and even i know that 0 == false.
jj4211@lemmy.world 2 days ago
In context, one can consider it a bool.
Besides, I see c code all the time that treats pointers as bool for the purposes of an if statement. !pointer is very common and no one thinks that means pointer it’s exclusively a Boolean concept.
sugar_in_your_tea@sh.itjust.works 2 days ago
Maybe, but that serves as a very valuable teaching opportunity about the concept of “empty” is in Python. It’s pretty intuitive IMO, and it can make a lot of things more clear once you understand that.
That said, larger projects should be using type hints everywhere, and that should make the intention here painfully obvious:
That’s obviously not a boolean, but it’s being treated as one. If the meaning there isn’t obvious, then look it up/ask someone about Python semantics.
I’m generally not a fan of learning a ton of jargon/big frameworks to get the benefits of more productivity (e.g. many design patterns are a bit obtuse IMO), but learning language semantics that are used pretty much everywhere seems pretty reasonable to me. And it’s a lot nicer than doing something like this everywhere:
if foo is None or len(foo) == 0:
JustAnotherKay@lemmy.world 2 days ago
Doesn’t matter what it implies. The entire purpose of programming is to make it so a human doesn’t have to go do something manually.
not x
tells me I need to go manually check what typex
is in Python.len(x) == 0
tells me that it’s being type-checked automaticallysugar_in_your_tea@sh.itjust.works 2 days ago
That’s just not true:
not x
- has an empty value (None, False,[]
,{}
, etc)len(x) == 0
- has a length (list
,dict
,tuple
, etc, or even a custom type implementing__len__
)You can probably assume it’s iterable, but that’s about it.
But why assume? You can easily just document the type with a type-hint: