Background: I was going to answer this question, starting with something like "The documentation specifies that non-empty lists are true and [...]". But then I realized that it doesn't specify that anymore. At least not obviously, which it used to.
Up to Python 3.5, the documentation still said (emphasis mine):
4.1. Truth Value Testing
Any object can be tested for truth value, for use in an if or while
condition or as operand of the Boolean operations below. The following
values are considered false:
None
False
zero of any numeric type, for example, 0, 0.0, 0j.
any empty sequence, for example, '', (), [].
any empty mapping, for example, {}.
instances of user-defined classes, if the class defines a __bool__() or __len__() method, when that method returns the integer zero or bool
value False. [1]
All other values are considered true — so objects of many types are always true.
Operations and built-in functions that have a Boolean result always return 0 or False for false and 1 or True for true, unless otherwise stated. (Important exception: the Boolean operations or and and always return one of their operands.)
A non-empty list like [5] doesn't fall under anything in the above list, so the "All other" specifies that it's true.
But since Python 3.6, that is gone. That section now says:
Truth Value Testing
Any object can be tested for truth value, for use in an if or while
condition or as operand of the Boolean operations below.
By default, an object is considered true unless its class defines
either a __bool__() method that returns False or a __len__() method
that returns zero, when called with the object. [1] Here are most of the
built-in objects considered false:
constants defined to be false: None and False.
zero of any numeric type: 0, 0.0, 0j, Decimal(0), Fraction(0, 1)
empty sequences and collections: '', (), [], {}, set(), range(0)
Operations and built-in functions that have a Boolean result always return 0 or False for false and 1 or True for true, unless otherwise stated. (Important exception: the Boolean operations or and and always return one of their operands.)
Now [5] could have a __bool__() method that returns False, and thus it would be false. Is there a new place in the current documentation that somehow specifies that non-empty lists are true?
The documentation of all the built-in classes list all the special methods that they implement. If a method isn't listed, you can assume it isn't implemented.
Since the documentation of list doesn't say anything about overriding the __bool__ method, it inherits the default behavior.
To find all the list operations, start here. However, it also points out that lists implement all the common and mutable sequence operations, so you'll need to read that documentation for the complete list.
Just found a place, in the reference (emphasis mine):
6.11. Boolean operations
[...]
In the context of Boolean operations, and also when expressions are
used by control flow statements, the following values are interpreted
as false: False, None, numeric zero of all types, and empty strings
and containers (including strings, tuples, lists, dictionaries, sets
and frozensets). All other values are interpreted as true.
User-defined objects can customize their truth value by providing a
__bool__() method.
It bothers me a little that that's right away contradicted by the very next sentence, about user-defined objects, but I'll take it.
[5] is a list object. Unless you have specifically overridden the built-in __bool__ method, you get the default method. As the documentation already implied, this is Truthy.
The update doesn't change things so much as widen the explanation to cover derived types and other augmentations of the built-in types.
Related
I recently found out that python has a special value NotImpemented to be used with respect to binary special methods to indicate that some operation has not been implemented.
The peculiar about this is that when checked in a binary situation it is always equivalent to True.
For example using io.BytesIO (which is a case where __eq__ in not implemented for example) for two objects in comparison will virtually return True. As in this example (encoded_jpg_io1 and encoded_jpg_io2 are objects of the io.BytesIO class):
if encoded_jpg_io1.__ne__(encoded_jpg_io2):
print('Equal')
else:
print('Unequal')
Equal
if encoded_jpg_io1.__eq__(encoded_jpg_io2) == True:
print('Equal')
else:
print('Unequal')
Unequal
Since the second style is a bit too verbose and normally not prefered (even my pyCharm suggests to remove the explicit comparison with True) isn't a bit tricky behavior? I wouldn't have noticed it if I haven't explicitly print the result of the Boolean operation (which is not Boolean in this case at all).
I guess suggesting to be considered False would cause the same problem with __ne__ so we arew back to step one.
So, the only way to check out for these cases is by doing an exact comparison with True or False in the opposite case.
I know that NotImpemented is preferred over NotImplementedError for various reasons so I am not asking for any explanation over why this matter.
Per convention, objects that do not define a __bool__ method are considered truthy. From the docs:
By default, an object is considered true unless its class defines either a __bool__() method that returns False or a __len__() method that returns zero
This means that most classes, functions, and other builtin singletons are considered true, since they don't go out of their way to specify different behavior. (An exception is None, which is one of the few built-in singletons that does specifically signal it should be considered false):
>>> bool(int) # the class, not an integer object
True
>>> bool(min)
True
>>> bool(object())
True
>>> bool(...) # that's the Ellipsis object
True
>>> bool(NotImplemented)
True
There is no real reason for the NotImplemented object to break this convention. The problem with your code isn't that NotImplemented is considered truthy; the real problem is that x.__eq__(y) is not equivalent to x == y.
If you want to compare two objects for equality, doing it with x.__eq__(y) is incorrect. Using x.__eq__(y) == True instead is still incorrect.
The correct solution is to do comparison with the == operator. If, for whatever reason, you can't use the == operator directly, you should use the operator.eq function instead.
I cannot find the method corresponding to not x operator. There is one for and, or, and xor, though. Where is it?
3. Data model
There are no hooks for and or or operators, no (as they short-circuit), and there is no xor operator in Python. The __and__ and __or__ are for the bitwise & and | operators, respectively. The equivalent bitwise operator for not is ~ (inversion), which is handled by the __invert__ method, while __xor__ covers the ^ bitwise operator.
not operates on the truth-value of an object. If you have a container, give it a __len__ method, if not give it a __bool__ method. Either one is consulted to determine if an object should be considered 'true'; not inverts the result of that test.
So if __bool__ returns True or __len__ returns an integer other than 0, not will invert that to False, otherwise not produces True. Note that you can't make not return anything else but a boolean value!
From the documentation for __bool__:
__bool__
Called to implement truth value testing and the built-in operation bool(); should return False or True. When this method is not defined, __len__() is called, if it is defined, and the object is considered true if its result is nonzero. If a class defines neither __len__() nor __bool__(), all its instances are considered true.>
and for the not expression:
In the context of Boolean operations, and also when expressions are used by control flow statements, the following values are interpreted as false: False, None, numeric zero of all types, and empty strings and containers (including strings, tuples, lists, dictionaries, sets and frozensets). All other values are interpreted as true. User-defined objects can customize their truth value by providing a __bool__() method.
The operator not yields True if its argument is false, False otherwise.
bold emphasis mine.
There is one for and, or, and xor, though
The methods you're looking at are for bitwise &, |, and ^, not and, or, or xor (which isn't even a Python operator).
not cannot be overloaded, just like and and or can't be overloaded. Bitwise ~ can be overloaded, though; that's __invert__.
If you're in a situation where you wish you could overload not, you'll either have to make do with overloading ~ instead, or you'll have to write your own logical_not function and use that instead of the not operator.
From what I have researched most operators and methods can be overridden when creating a class in python. By using __add__(self, other) and others for example.
My "problem" (more like I don't quite understand how it is done) is for verifying is something is in my class I have to obviously use __contains__(self, theThing).
Alas I thought this should return a boolean value in the code itself, but from example code I have seen, like this:
def __contains__(self, posORname):
return [node.getId() for node in self.tree if node.getId() == posORname or node.getName() == posORname]
What I am returning is therefore a tuple containing the Id of where said item is.
Could someone explain why this is done instead of returning True or False? And if so, shouldn't it be implicitly possible to get the index of an item in a structure by just using in?
Thanks :D
For python2 __contains__ or python3 __contains__ both should return true or false. And now the question is what is True and what is False. Truth value testing.
The following values are considered false:
None
False
zero of any numeric type, for example, 0, 0.0, 0j.
any empty sequence, for example, '', (), [].
any empty mapping, for example, {}.
instances of user-defined classes, if the class defines a bool() or len() method, when that method returns the integer zero or bool value False.
__contains__() is expected to return a boolean value. In your case, __contains__() is returning a list with any tree nodes that match posORname. Thus, it's basically just a lazy way of implementing __contains__(), because in a boolean context:
An empty list is equivalent to False.
A non-empty list is equivalent to True.
While you could potentially have __contains__() return a data structure with the index or ID of the matching tree node, the in operator doesn't care; it only exists to check whether or not an item is contained by an object. Also, __contains__() is not intended to be called directly, so relying on it to return such information would be an abuse of the __contains__() method.
Instead, you would be better off implementing an separate method for getting the index/id of a node.
The result of __contains__ is converted to a boolean according to the usual rules, so that, e.g., empty lists count as False while lists with something in them count as True. So for your example, if the list has anything in it --- that is, any items match the criteria in that list comprehension --- then the in test will be True.
This auto-conversion-to-bool behavior does not appear to be explicitly documented, and is different from other operators (like < and >), which return what they return without converting to bool. There is some discussion of the behavior here.
For one of my sites, I need to check if several class attributes are defined and not empty. So far, I've happily used if self.attr:, which in my mind is the shorthand for if self.attr is not None and self.attr is not '':, or whatever the undefined value of the attribute is.
This works fine, but yields to surprising behavior when checking multiple string attributes. '' and '' is not False (as I expected), but ''.
This begs the question: are there other types for which the and operator does not force a typecast to bool?
I can't come up with an example where this difference in behavior would cause an actual different outcome for the if-clause (after all, '' still evaluates to False), but am left with the gut feeling that there's edge cases that could be a trap.
Lastly, I'd be keen to know if anybody knows why it was implemented that way? I thought the Zen of Python encourages one way and one way only, and the + operator already seems to bethe intuitive way for string concatenation.
and never typecasts to bool. Rather, if calls bool() on the result of expressions.
An expression using and (and or, for that matter), short-circuits when it can determine that the expression will not evaluate to True or False based on the first operand, and returns the last evaluated value:
>>> 0 and 'string'
0
>>> 1 and 'string'
'string'
>>> 'string' or 10
'string'
>>> '' or 10
10
This 'side-effect' is often used in python code. Note that not does return a boolean value. See the python documentation on boolean operators for the details.
The documentation also explains what constitutes a True or False equivalent for various types, such as None, 0, '' and empty containers being False, while most everything else is equivalent to True.
For custom classes, you need to define the .__nonzero__() method (returns True or False) or a .__len__() method (returns an int, where 0 is False and everything else is True), to influence their boolean equivalent, otherwise they'll always default to True.
I was somewhat confused until I found the bug in my code. I had to change
a.matched_images.count #True when variable is 0
to
a.matched_images.count > 0 #False when variable is 0
Since I quickly wanted to know whether an object had any images, the first code will appear like the photo has images since the expression evaluates to True when the meaning really is false ("no images" / 0 images)
Did I understand this correctly and can you please answer or comment if these expressions should evaluate to different values.
What is the nature of count? If it's a basic Python number, then if count is the same as if count != 0. On the other hand, if count is a custom class then it needs to implement either __nonzero__ or __len__ for Python 2.x, or __bool__ or __len__ for Python 3.x. If those methods are not defined, then every instance of that class is considered True.
Without knowing what count is, it's hard to answer, but this excerpt may be of use to you:.
The following values are considered false:
None
False
zero of any numeric type, for example, 0, 0L, 0.0, 0j.
any empty sequence, for example, '', (), [].
any empty mapping, for example, {}.
instances of user-defined classes, if the class defines a
__nonzero__() or __len__() method, when that method returns the
integer zero or bool value False. [1]
All other values are considered true — so objects of many types are
always true.
>>> bool(0)
False
So.. no, if it were an int that wouldn't matter. Please do some tracing, print out what count actually is.