الفرق بين المراجعتين لصفحة: «Python/enum»

من موسوعة حسوب
لا ملخص تعديل
سطر 2: سطر 2:
enumeration هو مجموعة من الأسماء الرمزية (العناصر) المرتبطة بقيم ثابتة وفريدة. يمكن مقارنة عناصر enumeration عن طريق هويتها، ويمكن المرور على عناصر enumeration بواسطة حلقة تكرارية.
enumeration هو مجموعة من الأسماء الرمزية (العناصر) المرتبطة بقيم ثابتة وفريدة. يمكن مقارنة عناصر enumeration عن طريق هويتها، ويمكن المرور على عناصر enumeration بواسطة حلقة تكرارية.


== وحدة <code>enum</code> ==
تقدّم وحدة <code>enum</code> أربعة أصناف enumeration يمكن استخدامها لتعريف مجموعة فريدة من الأسماء والقيم، وهذه الأصناف هي: <code>Enum</code> و <code>IntEnum</code> و <code>Flag</code> و <code>IntFlag</code>. وإلى جانب ما سبق تقدّم الوحدة مزخرفًا واحدًا هو <code>unique()</code>‎،  وصنفًا مساعدًا واحدًا هو <code>auto</code>.
=== الصنف <code>enum.Enum</code> ===
هو الصنف الأساسي والذي يستخدم لإنشاء ثوابت معدّدة enumerated constants. راجع قسم الواجهة البرمجية الوظيفية للاطلاع على الصيغة البديلة.
=== الصنف <code>enum.IntEnum</code> ===
الصنف الأساسي الذي يستخدم لإنشاء ثوابت معدّدة تكون كذلك أصنافًا فرعية للصنف <code>[[Python/int|int]]</code>.
=== الصنف <code>enum.IntFlag</code> ===
الصنف الأساسي الذي يستخدم لإنشاء ثوابت معدّدة يمكن دمجها مع بعضها البعض باستخدم عامل bitwise دون التأثير على كونها عناصر للصنف <code>IntFlag</code>. وتكون عناصر <code>IntFlag</code> أصنافًا فرعية من الصنف <code>[[Python/int|int]]</code> أيضًا. 
=== الصنف <code>enum.Flag</code> ===
الصنف الرئيسي الذي يستخدم لإنشاء ثوابت معدّدة يمكن دمجها مع بعضها البعض دون التأثير على كونها من نوع <code>Flag</code>.
=== المزخرف <code>enum.unique()‎</code> ===
مزخرف الصنف Enum والذي يضمن ارتباط اسم واحد بقيمة واحدة فقط.
=== الصنف المساعد <code>enum.auto</code> ===
عند استخدام هذا الصنف تُستبدل نسخ الصنف Enum بقيم ملائمة.
'''ملاحظة''': الأصناف <code>Flag</code> و <code>IntFlag</code> و <code>auto</code> جديدة في الإصدار 3.6 من بايثون.
== إنشاء Enum ==
== إنشاء Enum ==
يمكن إنشاء Enumeration باستخدام الصيغة الخاصّة بإنشاء [[Python/class|الأصناف]] في بايثون، الأمر الذي يسهّل قراءة وكتابة هذا النوع من البيانات، ويوضح المثال التالي طريقة تعريف enumeration:<syntaxhighlight lang="python3">
يمكن إنشاء Enumeration باستخدام الصيغة الخاصّة بإنشاء [[Python/class|الأصناف]] في بايثون، الأمر الذي يسهّل قراءة وكتابة هذا النوع من البيانات، ويوضح المثال التالي طريقة تعريف enumeration:<syntaxhighlight lang="python3">

مراجعة 09:08، 12 يوليو 2018

enumeration هو مجموعة من الأسماء الرمزية (العناصر) المرتبطة بقيم ثابتة وفريدة. يمكن مقارنة عناصر enumeration عن طريق هويتها، ويمكن المرور على عناصر enumeration بواسطة حلقة تكرارية.

وحدة enum

تقدّم وحدة enum أربعة أصناف enumeration يمكن استخدامها لتعريف مجموعة فريدة من الأسماء والقيم، وهذه الأصناف هي: Enum و IntEnum و Flag و IntFlag. وإلى جانب ما سبق تقدّم الوحدة مزخرفًا واحدًا هو unique()‎، وصنفًا مساعدًا واحدًا هو auto.

الصنف enum.Enum

هو الصنف الأساسي والذي يستخدم لإنشاء ثوابت معدّدة enumerated constants. راجع قسم الواجهة البرمجية الوظيفية للاطلاع على الصيغة البديلة.

الصنف enum.IntEnum

الصنف الأساسي الذي يستخدم لإنشاء ثوابت معدّدة تكون كذلك أصنافًا فرعية للصنف int.

الصنف enum.IntFlag

الصنف الأساسي الذي يستخدم لإنشاء ثوابت معدّدة يمكن دمجها مع بعضها البعض باستخدم عامل bitwise دون التأثير على كونها عناصر للصنف IntFlag. وتكون عناصر IntFlag أصنافًا فرعية من الصنف int أيضًا. 

الصنف enum.Flag

الصنف الرئيسي الذي يستخدم لإنشاء ثوابت معدّدة يمكن دمجها مع بعضها البعض دون التأثير على كونها من نوع Flag.

المزخرف enum.unique()‎

مزخرف الصنف Enum والذي يضمن ارتباط اسم واحد بقيمة واحدة فقط.

الصنف المساعد enum.auto

عند استخدام هذا الصنف تُستبدل نسخ الصنف Enum بقيم ملائمة.

ملاحظة: الأصناف Flag و IntFlag و auto جديدة في الإصدار 3.6 من بايثون.

إنشاء Enum

يمكن إنشاء Enumeration باستخدام الصيغة الخاصّة بإنشاء الأصناف في بايثون، الأمر الذي يسهّل قراءة وكتابة هذا النوع من البيانات، ويوضح المثال التالي طريقة تعريف enumeration:

>>> from enum import Enum
>>> class Color(Enum):
...     RED = 1
...     GREEN = 2
...     BLUE = 3

ملاحظة: قيم عناصر Enumeration

يمكن استخدام أي نوع من البيانات (الأعداد الصحيحة، والسلاسل النصية) كقيمة لعناصر enumeration. إن كانت القيمة المضبوطة غير مهمّة فيمكن استخدام نسخ auto وستختار اللغة القيمة الملائمة، ولكن يجب الانتباه عند استخدام auto مع قيم أخرى.

ملاحظة: التسمية

  • الصنف Color هو enumeration (أو enum)
  • الخاصيات Color.RED، Color.GREEN وغيرها هي عناصر enumeration (أو عناصر enum) وهي قيم ثابتة.
  • تمتلك عناصر enum أسماء وقيم (اسم Color.RED هو RED، وقيمة Color.BLUE هي 3).

ملاحظة:

صحيح أن تعريف Enumerations يكون باستخدام صيغة تعريف الأصناف، إلا أنّ Enums لا تعدّ أصنافًا اعتيادية في بايثون. 

تمتلك عناصر Enumeration تمثيلًا نصّيًا قابلًا للقراءة:

>>> print(Color.RED)
Color.RED

في حين تعطي الدالة repr() معلومات أكثر:

>>> print(repr(Color.RED))
<Color.RED: 1>

أما نوع العنصر فهو الـ enumeration الذي ينتمي إليه:

>>> type(Color.RED)
<enum 'Color'>
>>> isinstance(Color.GREEN, Color)
True
>>>

تمتلك عناصر Enum خاصّية تحمل اسم العنصر فقط:

>>> print(Color.RED.name)
RED

تدعم Enumeration المرور على العناصر بحسب ترتيب تعريفها:

>>> class Shake(Enum):
...     VANILLA = 7
...     CHOCOLATE = 4
...     COOKIES = 9
...     MINT = 3
...
>>> for shake in Shake:
...     print(shake)
...
Shake.VANILLA
Shake.CHOCOLATE
Shake.COOKIES
Shake.MINT

عناصر Enumeration قابلة للتقطيع (hashable)؛ لذا يمكن استخدامها في القواميس والمجموعات:

>>> apples = {}
>>> apples[Color.RED] = 'red delicious'
>>> apples[Color.GREEN] = 'granny smith'
>>> apples == {Color.RED: 'red delicious', Color.GREEN: 'granny smith'}
True

الوصول إلى عناصر Enumeration وخواصّها برمجيًا

من المفيد في بعض الأحيان الوصول إلى عناصر Enumeration برمجيًّا (بمعنى أنّه لا يمكن استخدام أسماء محدّدة مثل Color.RED لأنّه اللون المطلوب قد يكون غير معروف أثناء كتابة البرنامج) ويمكن القيام بذلك كما يلي:

>>> Color(1)
<Color.RED: 1>
>>> Color(3)
<Color.BLUE: 3>

ويمكن الوصول إلى عناصر enum بواسطة أسمائها كما يلي:

>>> Color['RED']
<Color.RED: 1>
>>> Color['GREEN']
<Color.GREEN: 2>

للحصول على اسم أو قيمة عنصر من عناصر enum:

>>> member = Color.RED
>>> member.name
'RED'
>>> member.value
1

مضاعفة عناصر وقيم enum

لا تسمح اللغة بوجود عنصري enum يحملان نفس الاسم:

>>> class Shape(Enum):
...     SQUARE = 2
...     SQUARE = 3
...
Traceback (most recent call last):
...
TypeError: Attempted to reuse key: 'SQUARE'

ولكن يمكن لعنصري enum أن يمتلكا القيمة ذاتها. لو فرضنا وجود عنصرين A و B يحملان القيمة ذاتها (وكان A معرّفًا قبل B) فإن العنصر B سيكون اختصارًا (alias، أو اسمًا بديلًا) للعنصر A. إن جرى الاستعلام عن قيمة A و B فإنّ النتيجة ستكون A، وإن جرى الاستعلام عن اسم B فإن النتيجة ستكون A أيضًا:

>>> class Shape(Enum):
...     SQUARE = 2
...     DIAMOND = 1
...     CIRCLE = 3
...     ALIAS_FOR_SQUARE = 2
...
>>> Shape.SQUARE
<Shape.SQUARE: 2>
>>> Shape.ALIAS_FOR_SQUARE
<Shape.SQUARE: 2>
>>> Shape(2)
<Shape.SQUARE: 2>

ملاحظة: لا يمكن إنشاء عنصر يحمل اسم خاصّية معرّفة مسبقًا (عنصر آخر، أو تابع ...إلخ.) كما لا يمكن إنشاء خاصّية تحمل نفس اسم عنصر معرّف مسبقًا.

ضمان فرادة قيم enumeration

تسمح enumerations - افتراضيًّا - باستخدام أسماء متعددة كمختصرات للقيمة نفسها. إن كان هذا الأسلوب غير مرغوب به فيمكن استخدام المزخرف التالي لضمان استخدام كلّ قيمة في الـ enumeration لمرة واحدة فقط:

@enum.unique

يوضح المثال التالي كيفية استخدام هذا المزخرف (decorator):

>>> from enum import Enum, unique
>>> @unique
... class Mistake(Enum):
...     ONE = 1
...     TWO = 2
...     THREE = 3
...     FOUR = 3
...
Traceback (most recent call last):
...
ValueError: duplicate values found in <enum 'Mistake'>: FOUR -> THREE

استخدام القيم التلقائية

إن كانت القيمة الدقيقة غير مهمّة فيمكن استخدام auto محلّها:

>>> from enum import Enum, auto
>>> class Color(Enum):
...     RED = auto()
...     BLUE = auto()
...     GREEN = auto()
...
>>> list(Color)
[<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]

تختار اللغة القيم عن طريق التابع ‎_generate_next_value_()‎ والذي يمكن إعادة تعريفه (override):

>>> class AutoName(Enum):
...     def _generate_next_value_(name, start, count, last_values):
...         return name
...
>>> class Ordinal(AutoName):
...     NORTH = auto()
...     SOUTH = auto()
...     EAST = auto()
...     WEST = auto()
...
>>> list(Ordinal)
[<Ordinal.NORTH: 'NORTH'>, <Ordinal.SOUTH: 'SOUTH'>, <Ordinal.EAST: 'EAST'>, <Ordinal.WEST: 'WEST'>]

المرور على عناصر enumeration

لا تقدم عملية المرور على عناصر enumeration الاختصارات الموجودة فيه:

>>> list(Shape)
[<Shape.SQUARE: 2>, <Shape.DIAMOND: 1>, <Shape.CIRCLE: 3>]

الخاصّية الخاصة __members__ هي قاموس مرتّب يربط الأسماء بالعناصر، ويتضمن جميع الأسماء المعرّفة في enumeration وبضمنها الاختصارات:

>>> for name, member in Shape.__members__.items():
...     name, member
...
('SQUARE', <Shape.SQUARE: 2>)
('DIAMOND', <Shape.DIAMOND: 1>)
('CIRCLE', <Shape.CIRCLE: 3>)
('ALIAS_FOR_SQUARE', <Shape.SQUARE: 2>)

يمكن الاستفادة من الخاصية __members__ للوصول إلى عناصر enumeration برمجيًّا. فعلى سبيل المثال يمكن العثور على جميع الاختصارات بالطريقة التالية:

>>> [name for name, member in Shape.__members__.items() if member.name != name]
['ALIAS_FOR_SQUARE']

مقارنة العناصر

يمكن عقد المقارنات بين عناصر enumeration باستخدام هوية العنصر:

>>> Color.RED is Color.RED
True
>>> Color.RED is Color.BLUE
False
>>> Color.RED is not Color.BLUE
True

لا تدعم اللغة المقارنات المرتّبة بين قيم enumeration، إلى جانب أنّ عناصر Enum ليست أعدادًا صحيحة.

>>> Color.RED < Color.BLUE
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'Color' and 'Color'

ولكن يمكن إجراء اختبارات المساواة:

>>> Color.BLUE == Color.RED
False
>>> Color.BLUE != Color.RED
True
>>> Color.BLUE == Color.BLUE
True

عقد المقارنة مع قيم ليست ضمن enumeration تكون نتيجتها دومًا عدم المساواة:

>>> Color.BLUE == 2
False

العناصر والخصائص المسموح بها في enumeration

في الأمثلة السابقة كانت قيم enumeration أعدادًا صحيحة. بالرغم من أن استخدام الأعداد الصحيحة مختصر وسريع، ولكنّه ليس إلزاميًا. في أغلب الحالات ليس من المهمّ معرفة القيمة الحقيقية للـ enumeration، ولكن إن كانت القيمة مهمّة، فيمكن للـ enumerations أن تمتلك أيّ قيمة ممكنة.

Enumerations هي أصناف بايثون؛ لذا يمكن أن تمتلك توابع وتوابع خاصّة كما هو الحال مع الأصناف، فلو كان لدينا الـ enumeration التالي:

>>> class Mood(Enum):
...     FUNKY = 1
...     HAPPY = 3
...
...     def describe(self):
...         # self is the member here
...         return self.name, self.value
...
...     def __str__(self):
...         return 'my custom str! {0}'.format(self.value)
...
...     @classmethod
...     def favorite_mood(cls):
...         # cls here is the enumeration
...         return cls.HAPPY
...

فيمكن حينئذ كتابة الشيفرة التالية:

>>> Mood.favorite_mood()
<Mood.HAPPY: 3>
>>> Mood.HAPPY.describe()
('HAPPY', 3)
>>> str(Mood.FUNKY)
'my custom str! 1'

تتبع عملية التسمية القواعد التالية:

  • تكون الأسماء التي تبدأ وتنتهي بشرطة سفلية واحدة محجوزة من طرف enum ولا يمكن استخدامها. 
  • تصبح جميع الخاصيات الأخرى المعرّفة ضمن enumeration عناصر له؛ باستثناء التوابع الخاصة (‎__str__()، __add__()‎، وغيرها) والواصفات (descriptors) (التوابع واصفات أيضًا).

انظر أيضًا

مصادر