@ Pythonetc-Zusammenstellung, August 2019



Eine neue Auswahl an Python-Tipps und -Programmierungen aus meinem @ pythonetc-Feed.

Frühere Sammlungen


Wenn die Klasseninstanz kein Attribut mit dem angegebenen Namen hat, versucht sie, auf das Klassenattribut mit demselben Namen zuzugreifen.

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

Eine Instanz kann leicht ein Attribut haben, das die Klasse nicht hat, oder ein Attribut mit einem anderen Wert:

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

Wenn Sie möchten, dass sich die Instanz so verhält, als hätte sie kein Attribut, obwohl die Klasse es hat, müssen Sie einen benutzerdefinierten Deskriptor erstellen, der den Zugriff von dieser Instanz aus verbietet:

 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(Ax) # 2 A().x # raises AttributeError 

Sehen Sie auch, wie die classonlymethod Django Decorator classonlymethod : https://github.com/django/django/blob/b709d701303b3877387020c1558a590713b09853/django/utils/decorators.py#L6


Auf die im Klassenkörper deklarierten Funktionen kann der Bereich dieser Klasse nicht zugreifen. Dies liegt daran, dass dieser Bereich nur während der Erstellung der Klasse vorhanden ist.

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

Dies ist normalerweise kein Problem: Methoden werden nur innerhalb der Klasse deklariert, um Methoden zu werden und später aufgerufen zu werden:

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

Überraschenderweise gilt das Gleiche für das Verständnis. Sie haben ihren eigenen Bereich und sie haben auch keinen Zugriff auf den Bereich der Klassen. Dies ist im Hinblick auf das Generatorverständnis sehr logisch: Der darin enthaltene Code wird ausgeführt, wenn die Klasse bereits erstellt wurde.

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

Verständnis hat jedoch keinen Zugang zu sich self . Die einzige Möglichkeit, Zugriff auf x zu gewähren, besteht darin, einen weiteren Bereich hinzuzufügen (ja, dumme Lösung):

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


In Python ist None gleich None , sodass Sie möglicherweise mit == : nach 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)) 

Aber das wird ein Fehler sein. Ja, None ist gleich None , aber nicht nur das. Benutzerdefinierte Objekte können auch None :

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

Der einzig richtige Weg, um mit None zu vergleichen, is None .



Gleitkommazahlen in Python können NaN-Werte haben. Zum Beispiel kann eine solche Zahl mit math.nan erhalten werden. nan nichts gleich, auch sich selbst:

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

Darüber hinaus ist ein NaN-Objekt nicht eindeutig. Sie können mehrere verschiedene NaN-Objekte aus verschiedenen Quellen verwenden:

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

Dies bedeutet, dass Sie NaN im Allgemeinen nicht als Wörterbuchschlüssel verwenden können:

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


typing können Sie Typen für Generatoren definieren. Darüber hinaus können Sie angeben, welcher Typ generiert, welcher an den Generator übergeben und welcher mit return . Beispielsweise generiert Generator[int, None, bool] Ganzzahlen, gibt Boolesche Werte zurück und unterstützt g.send() .

Das Beispiel ist jedoch komplizierter. chain_while Daten von anderen Generatoren, bis einer von ihnen einen Wert zurückgibt, der ein Stoppsignal gemäß der condition ist:

 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, ))) 


Das Festlegen von Anmerkungen für die Factory-Methode ist nicht so einfach, wie es scheint. Ich möchte nur so etwas verwenden:

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

Aber es wird falsch sein. Der Trick ist, dass create nicht A zurückgibt, sondern cls , das A oder einer seiner Nachkommen ist. Schauen Sie sich den Code an:

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

Das Ergebnis der mypy-Prüfung ist der Fehlerfehler error: Incompatible return value type (got "A", expected "B") . Wieder ist das Problem, dass super().create() als Rückgabe von A kommentiert wird, obwohl es in diesem Fall B zurückgibt B

Dies kann behoben werden, wenn TypeVar mit TypeVar :

 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 jetzt eine Instanz der cls Klasse zurück. Diese Anmerkungen sind jedoch zu vage. Wir haben Informationen verloren, dass cls ein Subtyp von A :

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

Wir "Type[AType]" has no attribute "DATA" Fehler "Type[AType]" has no attribute "DATA" .

Um dies zu beheben, müssen Sie AType explizit als Subtyp von A Hierzu wird TypeVar mit dem bound Argument verwendet.

 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() 

Source: https://habr.com/ru/post/de466315/


All Articles