
مجموعة جديدة من نصائح Python والبرمجة من خلال موجز pythonetc الخاص بي.
←
المجموعات السابقةإذا لم يكن لدى مثيل الفئة سمة تحمل الاسم المحدد ، فسيحاول الوصول إلى سمة الفصل بنفس الاسم.
>>> class A: ... x = 2 ... >>> Ax 2 >>> A().x 2
يمكن أن يكون للمثيل سمة لا يمتلكها الفصل بسهولة ، أو يكون له سمة ذات قيمة مختلفة:
>>> 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'
إذا كنت تريد أن يتصرف المثيل كما لو أنه لا يحتوي على سمة ، على الرغم من أن الفصل لديه ، فسيتعين عليك إنشاء واصف مخصص يحظر الوصول من هذا المثيل:
class ClassOnlyDescriptor: def __init__(self, value): self._value = value self._name = None
انظر أيضًا كيف
classonlymethod
مصمم الديكور
classonlymethod
Django:
https://github.com/django/django/blob/b709d701303b3877387020c1558a590713b09853/django/utils/decorators.py#L6لا يمكن الوصول إلى الوظائف المعلنة في نص الفصل إلى نطاق هذه الفئة. هذا لأن هذا النطاق موجود فقط أثناء إنشاء الفصل.
>>> 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
. الطريقة الوحيدة لتوفير الوصول إلى
x
هي إضافة نطاق آخر (نعم ، حل غبي):
>>> class A: ... x = 2 ... y = (lambda x=x: [x for _ in range(5)])() ... >>> Ay [2, 2, 2, 2, 2]
في بيثون ،
None
ما يعادل
None
، لذلك قد يبدو أنه يمكنك البحث عن بلا مع
==
:
ES_TAILS = ('s', 'x', 'z', 'ch', 'sh') def make_plural(word, exceptions=None): if exceptions == None:
لكن ذلك سيكون خطأ. نعم ،
None
يساوي
None
، ولكن ليس هذا فقط. الكائنات المخصصة يمكن أن تكون أيضًا بلا:
>>> class A: ... def __eq__(self, other): ... return True ... >>> A() == None True >>> A() is None False
الطريقة الصحيحة الوحيدة للمقارنة مع
None
هي استخدام
is None
.
يمكن أن تحتوي أرقام الفاصلة العائمة في Python على قيم NaN. على سبيل المثال ، يمكن الحصول على هذا الرقم باستخدام
math.nan
.
nan
لا يساوي أي شيء ، بما في ذلك نفسه:
>>> math.nan == math.nan False
بالإضافة إلى ذلك ، كائن NaN ليس فريدًا ؛ يمكنك الحصول على العديد من كائنات NaN مختلفة من مصادر مختلفة:
>>> float('nan') nan >>> float('nan') is float('nan') False
هذا يعني أنه ، بشكل عام ، لا يمكنك استخدام NaN كمفتاح قاموس:
>>> d = {} >>> d[float('nan')] = 1 >>> d[float('nan')] = 2 >>> d {nan: 1, nan: 2}
تتيح لك
typing
تحديد أنواع المولدات. بالإضافة إلى ذلك ، يمكنك تحديد أي نوع يتم إنشاؤه ، والذي يتم تمريره إلى المولد ، والذي يتم إرجاعه باستخدام
return
. على سبيل المثال ، ينشئ
Generator[int, None, bool]
أعدادًا صحيحة ، ويعيد Booleans ، ولا يدعم
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
، فهي ترجع
cls
، التي هي
A
أو أحد أحفادها. ألقِ نظرة على الكود:
class A: @classmethod def create(cls) -> 'A': return cls() class B(A): @classmethod def create(cls) -> 'B': return super().create()
نتيجة فحص mypy هي خطأ في
error: Incompatible return value type (got "A", expected "B")
. مرة أخرى ، تكمن المشكلة في أن
super().create()
موضحة على أنها عائد
A
، على الرغم من أنها في هذه الحالة تُرجع
B
يمكن إصلاح ذلك في حالة إضافة تعليقات
cls
باستخدام
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
الآن إرجاع مثيل لفئة
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"
الخطأ
"Type[AType]" has no attribute "DATA"
.
لإصلاحها ، يجب عليك تعريف
AType
بشكل صريح
AType
فرعي من
A
لهذا ، يتم استخدام
TypeVar
مع الوسيطة
bound
.
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()