>>> class A: ... x = 2 ... >>> A.x 2 >>> A().x 2

>>> class A: ... x = 2 ... def __init__(self): ... self.x = 3 ... self.y = 4 ... >>> A().x 3 >>> A.x 2 >>> A().y 4 >>> A.y AttributeError: type object 'A' has no attribute 'y'

class ClassOnlyDescriptor: def __init__(self, value): self._value = value self._name = None # see __set_name__ def __get__(self, instance, owner): if instance is not None: raise AttributeError( f'{instance} has no attribute {self._name}' ) return self._value def __set_name__(self, owner, name): self._name = name class_only = ClassOnlyDescriptor class A: x = class_only(2) print(A.x) # 2 A().x # raises AttributeError

classonlymethod

>>> class A: ... x = 2 ... def f(): ... print(x) ... f() ... [...] NameError: name 'x' is not defined

>>> class A: ... x = 2 ... def f(self): ... print(self.x) ... >>> >>> >>> A().f() 2

>>> class A: ... x = 2 ... y = [x for _ in range(5)] ... [...] NameError: name 'x' is not defined

self

>>> class A: ... x = 2 ... y = (lambda x=x: [x for _ in range(5)])() ... >>> A.y [2, 2, 2, 2, 2]

None

None

None

==

ES_TAILS = ('s', 'x', 'z', 'ch', 'sh') def make_plural(word, exceptions=None): if exceptions == None: # ← ← ← exceptions = {} if word in exceptions: return exceptions[word] elif any(word.endswith(t) for t in ES_TAILS): return word + 'es' elif word.endswith('y'): return word[0:-1] + 'ies' else: return word + 's' exceptions = dict( mouse='mice', ) print(make_plural('python')) print(make_plural('bash')) print(make_plural('ruby')) print(make_plural('mouse', exceptions=exceptions))

None

None

None

>>> class A: ... def __eq__(self, other): ... return True ... >>> A() == None True >>> A() is None False

None

is None

math.nan

nan

>>> math.nan == math.nan False

>>> float('nan') nan >>> float('nan') is float('nan') False

>>> d = {} >>> d[float('nan')] = 1 >>> d[float('nan')] = 2 >>> d {nan: 1, nan: 2}

typing

Generator[int, None, bool]

g.send()

chain_while

condition

from typing import Generator, Callable, Iterable, TypeVar Y = TypeVar('Y') S = TypeVar('S') R = TypeVar('R') def chain_while( iterables: Iterable[Generator[Y, S, R]], condition: Callable[[R], bool], ) -> Generator[Y, S, None]: for it in iterables: result = yield from it if not condition(result): break def r(x: int) -> Generator[int, None, bool]: yield from range(x) return x % 2 == 1 print(list(chain_while( [ r(5), r(4), r(3), ], lambda x: x is True, )))

class A: @classmethod def create(cls) -> 'A': return cls()

create

A

A

class A: @classmethod def create(cls) -> 'A': return cls() class B(A): @classmethod def create(cls) -> 'B': return super().create()

error: Incompatible return value type (got "A", expected "B")

super().create()

A

B

AType = TypeVar('AType') BType = TypeVar('BType') class A: @classmethod def create(cls: Type[AType]) -> AType: return cls() class B(A): @classmethod def create(cls: Type[BType]) -> BType: return super().create()

create

cls

cls

A

AType = TypeVar('AType') class A: DATA = 42 @classmethod def create(cls: Type[AType]) -> AType: print(cls.DATA) return cls()

"Type[AType]" has no attribute "DATA"

AType

A

bound

TypeVar

AType = TypeVar('AType', bound='A') BType = TypeVar('BType', bound='B') class A: DATA = 42 @classmethod def create(cls: Type[AType]) -> AType: print(cls.DATA) return cls() class B(A): @classmethod def create(cls: Type[BType]) -> BType: return super().create()

It is a new selection of tips and tricks about Python and programming from my Telegram-channel @pythonetc.If an instance of a class doesn’t have an attribute with the given name, it tries to access the class attribute with the same name.It’s fairly simple for an instance to have attribute that a class doesn’t or have the attribute with the different value:If it’s not that simple, however, if you want an instance behave like it doesn’t have an attribute despite the class having it. To make it happen you have to create custom descriptor that doesn’t allow access from the instance:See also how the Djangodecorator works: https://github.com/django/django/blob/b709d701303b3877387020c1558a590713b09853/django/utils/decorators.py#L6 Functions declared in a class body can’t see the class scope. It makes sense since the class scope only exists during class creation.That is usually not a problem: methods are declared inside a class only to become methods and be called later:Somewhat surprisingly, the same is true for comprehensions. They have their own scopes and can’t access the class scope as well. That really make sense for generator comprehensions: they evaluate expressions after the class creation is already finished.Comprehensions, however, have no access to. The only way to make it work is to add one more scope (yep, that’s ugly):In Python,is equal toso it looks like you can check forwithThis is a wrong thing to do though.is indeed is equal to, but it’s not the only thing that is. Custom objects may be equal totoo:The only proper way to compare withis to usePython floats can have NaN values. You can get one withis not equal to anything including itself:Also, NaN object is not unique, you can have several different NaN objects from different sources:That means that you generally can’t use NaN as a dictionary key:allows you to define type for generators. You can additionally specify what type is yielded, what type can be sent into a generator and what is returned.is a generator that yields integers, returns boolean value and doesn’t supportHere is slightly more complicated example.yields from other generators until one of them returns something that is a signal to stop according to thefunction:Annotating a factory method is not as simple as it may seem. The immediate urge is to use something like this:However, that is not a right thing to do. The catch is,doesn’t return, it returns an instance of cls that isany of its descendants. Look at this code:The mypy check result is. Again, the problem isis annotated to returnwhile it clearly returnsin this case.You can fix that by annotating cls with TypeVar:Nowreturns the instance of theclass. However, this annotations are too loose, we lost the information thatis a subtype ofThe error isTo fix that you have to explicitly defineas a subtype ofwith theargument of