
Eine neue Auswahl an Python-Tipps und -Programmierungen aus meinem @ pythonetc-Feed.
←
Frühere SammlungenWenn 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
Sehen Sie auch, wie die
classonlymethod
Django Decorator
classonlymethod
:
https://github.com/django/django/blob/b709d701303b3877387020c1558a590713b09853/django/utils/decorators.py#L6Auf 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:
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()