Understanding not in Python

Hi, Habr. In anticipation of the start of the course “Python Developer. Professional ” prepared a traditional translation of useful material.



We also invite everyone to visit the open webinar on
"Data visualization with matplotlib".






Python , , , . not



.





:





not True, False, False .





, ? , «» «», , , .





( , , « », , )





not

, , not



– UNARY_NOT. 





not a



:





>>> import dis
>>> def spam(): not a
... 
>>> dis.dis(spam)
  1           0 LOAD_GLOBAL              0 (a)
              2 UNARY_NOT
              4 POP_TOP
              6 LOAD_CONST               0 (None)
              8 RETURN_VALUE
      
      



UNARY_NOT , PyObject_IsTrue()



: True False, False True.





UNARY_NOT Python/ceval.c:





case TARGET(UNARY_NOT): {
            PyObject *value = TOP();
            int err = PyObject_IsTrue(value);
            Py_DECREF(value);
            if (err == 0) {
                Py_INCREF(Py_True);
                SET_TOP(Py_True);
                DISPATCH();
            }
            else if (err > 0) {
                Py_INCREF(Py_False);
                SET_TOP(Py_False);
                DISPATCH();
            }
            STACK_SHRINK(1);
            goto error;
        }
      
      



, True

not



, True. PyObject_IsTrue()



, , .





PyObject_IsTrue():





/* Test a value used as condition, e.g., in a for or if statement.
   Return -1 if an error occurred */


int
PyObject_IsTrue(PyObject *v)
{
    Py_ssize_t res;
    if (v == Py_True)
        return 1;
    if (v == Py_False)
        return 0;
    if (v == Py_None)
        return 0;
    else if (v->ob_type->tp_as_number != NULL &&
             v->ob_type->tp_as_number->nb_bool != NULL)
        res = (*v->ob_type->tp_as_number->nb_bool)(v);
    else if (v->ob_type->tp_as_mapping != NULL &&
             v->ob_type->tp_as_mapping->mp_length != NULL)
        res = (*v->ob_type->tp_as_mapping->mp_length)(v);
    else if (v->ob_type->tp_as_sequence != NULL &&
             v->ob_type->tp_as_sequence->sq_length != NULL)
        res = (*v->ob_type->tp_as_sequence->sq_length)(v);
    else
        return 1;
    /* if it is negative, it should be either -1 or -2 */
    return (res > 0) ? 1 : Py_SAFE_DOWNCAST(res, Py_ssize_t, int);
}
      
      



, :





  1. True, True





  2. False, False





  3. None, False





  4. , bool



    , , bool



    (, nb_bool



    )





  5. len()



    (, mp_length



    sq_length



    ):





    1. 0, True





    2. False





  6. , True





1-3 6 , 4 5 .





bool







/ bool , « » True, False.





len()







len()



, . sq_length



( ) mp_length



( /).





, , .





len 







/ len



. , « , >= 0». , «» int



, , « »… « ». ?





index







« », / index



. , PyNumber_Index()



. , , :





  1. int



    ,





  2. index







  3. index



    int



    , ( , ).





  4. TypeError



    .





Python operator.index()



. , PyNumber_Index()



, not



len



, . , :





PyNumber_Index()



Python:





def index(obj: Object, /) -> int:
    """Losslessly convert an object to an integer object.

    If obj is an instance of int, return it directly. Otherwise call __index__()
    and require it be a direct instance of int (raising TypeError if it isn't).
    """
    # https://github.com/python/cpython/blob/v3.8.3/Objects/abstract.c#L1260-L1302
    if isinstance(obj, int):
        return obj

    length_type = builtins.type(obj)
    try:
        __index__ = _mro_getattr(length_type, "__index__")
    except AttributeError:
        msg = (
            f"{length_type!r} cannot be interpreted as an integer "
            "(must be either a subclass of 'int' or have an __index__() method)"
        )
        raise TypeError(msg)
    index = __index__(obj)
    # Returning a subclass of int is deprecated in CPython.
    if index.__class__ is int:
        return index
    else:
        raise TypeError(
            f"the __index__() method of {length_type!r} returned an object of "
            f"type {builtins.type(index).__name__!r}, not 'int'"
        )
      
      



len()

len()



: int



. , , index()



len()



, PyLong_FromSsize_t()



, int



.





len()



, len()



index ()



, , int



, 0 .. , len()



:





def len(obj: Object, /) -> int:
    """Return the number of items in a container."""
    # https://github.com/python/cpython/blob/v3.8.3/Python/bltinmodule.c#L1536-L1557
    # https://github.com/python/cpython/blob/v3.8.3/Objects/abstract.c#L45-L63
    # https://github.com/python/cpython/blob/v3.8.3/Objects/typeobject.c#L6184-L6209
    type_ = builtins.type(obj)
    try:
        __len__ = _mro_getattr(type_, "__len__")
    except AttributeError:
        raise TypeError(f"type {type!r} does not have a __len__() method")
    length = __len__(obj)
    # Due to len() using PyObject_Size() (which returns Py_ssize_t),
    # the returned value is always a direct instance of int via
    # PyLong_FromSsize_t().
    index = int(_index(length))
    if index < 0:
        raise ValueError("__len__() should return >= 0")
    else:
        return index
      
      



operator.truth()

not, not



–  not not



. , , , , , .





Python . bool()



( bool.new()



), , , , operator.truth()



. , , PyObject_IsTrue()



. slot_nb_bool ()



, , , PyObject_IsTrue()



. , PyObject_IsTrue()



, , . 





, , operator.truth()



( bool



, , True False , 1 0 Python).





operator.truth()



:





def truth(obj: Any, /) -> bool:
    """Return True if the object is true, False otherwise.

    Analogous to calling bool().

    """
    if obj is True:
        return True
    elif obj is False:
        return False
    elif obj is None:
        return False
    obj_type = type(obj)
    try:
        __bool__ = debuiltins._mro_getattr(obj_type, "__bool__")
    except AttributeError:
        # Only try calling len() if it makes sense.
        try:
            __len__ = debuiltins._mro_getattr(obj_type, "__len__")
        except AttributeError:
            # If all else fails...
            return True
        else:
            return True if debuiltins.len(obj) > 0 else False
    else:
        boolean = __bool__(obj)
        if isinstance(boolean, bool):
            # Coerce into True or False.
            return truth(boolean)
        else:
            raise TypeError(
                f"expected a 'bool' from {obj_type.__name__}.__bool__(), "
                f"not {type(boolean).__name__!r}"
            )
      
      



not

operator.truth()



, operator.not_()



– :





lambda a, /: False if truth(a) else True
      
      



, , , .





, desugar






«Python Developer. Professional».





« matplotlib».








All Articles