Python Cookbook 2Nd Edition Jun 1002005 [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Python Cookbook 2Nd Edition Jun 1002005 [Electronic resources] - نسخه متنی

David Ascher, Alex Martelli, Anna Ravenscroft

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید







Recipe 16.9. Simulating Enumerations in Python


Credit: Will Ware


Problem


You want to define an enumeration in
the spirit of C's enum type.


Solution


Python's introspection facilities let you code a
class that implements a version of enum, even
though Python, as a language, does not support the
enum construct:

class EnumException(Exception):
pass
class Enumeration(object):
def _ _init_ _(self, name, enumList, valuesAreUnique=True):
self._ _doc_ _ = name
self.lookup = lookup = { }
self.reverseLookup = reverseLookup = { }
i = 0
for x in enumList:
if type(x) is tuple:
try:
x, i = x
except ValueError:
raise EnumException, "tuple doesn't have 2 items: %r" % (x,)
if type(x) is not str:
raise EnumException, "enum name is not a string: %r" % (x,)
if type(i) is not int:
raise EnumException, "enum value is not an integer: %r" % (i,)
if x in lookup:
raise EnumException, "enum name is not unique: %r" % (x,)
if valuesAreUnique and i in reverseLookup:
raise EnumException, "enum value %r not unique for %r" % (i, x)
lookup[x] = i
reverseLookup[i] = x
i = i + 1
def _ _getattr_ _(self, attr):
try: return self.lookup[attr]
except KeyError: raise AttributeError, attr
def whatis(self, value):
return self.reverseLookup[value]


Discussion


In the C language, enum lets you declare several
named constants, typically with unique values (although you can also
explicitly arrange for a value to be duplicated under two different
names), without necessarily specifying the actual values (except when
you want it to). Despite the similarity in naming,
C's enum and this
recipe's Enumeration class have
little to do with the Python built-in enumerate
generator, which is used to loop on
(index,
item) pairs
given an iterablean entirely different issue!

Python has an accepted idiom that's fine for small
numbers of constants:

A, B, C, D = range(4)

However, this idiom doesn't scale well to large
numbers of constants and doesn't allow you to
specify values for some constants while leaving others to be
determined automatically. This recipe provides for all these niceties
and, optionally, also checks that all values (both the ones
explicitly specified and the ones automatically determined) are
unique. Enum values are attributes of an Enumeration
class instance (Volkswagen.BEETLE,
Volkswagen.PASSAT, etc.). A further feature, missing
in C but really quite useful, is the ability to go from the value to
the corresponding name inside the enumeration (the name you get can
be somewhat arbitrary for those enumerations in which you
don't constrain values to be unique).

This recipe's Enumeration class has
an initializer that accepts a string argument to specify the
enumeration's name and a sequence argument to
specify the names of all values in the enumeration. Each item of the
sequence argument can be a string (to specify that the value named is
one more than the last value used) or else a tuple with two items
(the string that is the value's name, then the value
itself, which must be an integer). The code in this recipe relies
heavily on strict type checking to determine which case applies, but
the recipe's essence would not change by much if the
checking was performed in a more lenient way (e.g., with the
isinstance built-in function).

Each Enumeration instance has two
dict attributes: self.lookup to
map names to values and self.reverselookup to map
values back to the corresponding names. The special method _
_getattr_ _
lets you use names with attribute syntax
(e.x is mapped to e.lookup['x']),
and the whatis method allows reverse lookups (i.e.,
find a name given a value) with similar ease.

Here's an example of how you can use this
Enumeration class:

if _ _name_ _ == '_ _main_ _':
import pprint
Volkswagen = Enumeration("Volkswagen",
("JETTA", "RABBIT", "BEETLE", ("THING", 400), "PASSAT", "GOLF",
("CABRIO", 700), "EURO_VAN", "CLASSIC_BEETLE", "CLASSIC_VAN"
))
Insect = Enumeration("Insect",
("ANT", "APHID", "BEE", "BEETLE", "BUTTERFLY", "MOTH", "HOUSEFLY",
"WASP", "CICADA", "GRASSHOPPER", "COCKROACH", "DRAGONFLY"
))
def whatkind(value, enum):
return enum._ _doc_ _ + "." + enum.whatis(value)
class ThingWithKind(object):
def _ _init_ _(self, kind):
self.kind = kind
car = ThingWithKind(Volkswagen.BEETLE)
print whatkind(car.kind, Volkswagen)
# emits Volkswagen.BEETLE
bug = ThingWithKind(Insect.BEETLE)
print whatkind(bug.kind, Insect)
# emits Insect.BEETLE
print car._ _dict_ _
# emits {'kind': 2}
print bug._ _dict_ _
# emits {'kind': 3}
pprint.pprint(Volkswagen._ _dict_ _)
pprint.pprint(Insect._ _dict_ _)
# emits dozens of line showing off lookup and reverseLookup dictionaries

Note that the attributes of car and
bug don't include any of
the enum machinery because that machinery is held
as class attributes, not as instance attributes. This means you can
generate thousands of car and
bug objects with reckless abandon, never
worrying about wasting time or memory on redundant copies of the
enum stuff.


See Also


Recipe 6.2 shows how to
define constants in Python; documentation on the special method
_ _getattr_ _ in the Language
Reference
and Python in a
Nutshell
.


/ 394