程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

When does Python generate PyC files?

編輯:Python

Duplicate member names are not allowed
My first idea in this part is to control dict Medium key . But this way is not good ,__dict__ Large scope , It contains all the properties and methods of this class . Not just the namespace of enumeration . I found in the source code enum Use another method . adopt prepare Magic method can return an instance of class dictionary , In this instance Use prepare Magic method custom namespace , The qualified member name cannot be repeated in this space .

# Realize it by yourself
class _Dict(dict):
def __setitem__(self, key, value):
if key in self:
raise TypeError('Attempted to reuse key: %r' % key)
super().__setitem__(key, value)
class MyMeta(type):
@classmethod
def __prepare__(metacls, name, bases):
d = _Dict()
return d
class Enum(metaclass=MyMeta):
pass
class Color(Enum):
red = 1
red = 1 # TypeError: Attempted to reuse key: 'red'

I want to see others Enum The concrete implementation of the module :

class _EnumDict(dict):
def __init__(self):
super().__init__()
self._member_names = []
...
def __setitem__(self, key, value):
...
elif key in self._member_names:
# descriptor overwriting an enum?
raise TypeError('Attempted to reuse key: %r' % key)
...
self._member_names.append(key)
super().__setitem__(key, value)
class EnumMeta(type):
@classmethod
def __prepare__(metacls, cls, bases):
enum_dict = _EnumDict()
...
return enum_dict
class Enum(metaclass=EnumMeta):
...

Module _EnumDict Created _member_names List to store member names , This is because not all members in the namespace are enumeration members . such as str__, __new Wait for magic method is not , So this way setitem You need to do some filtering :

def __setitem__(self, key, value):
if _is_sunder(key): # Underline the beginning and end of , Such as _order__
raise ValueError('_names_ are reserved for future Enum use')
elif _is_dunder(key): # Double underlined , Such as __new__
if key == '__order__':
key = '_order_'
elif key in self._member_names: # Repeatedly defined key
raise TypeError('Attempted to reuse key: %r' % key)
elif not _is_descriptor(value): # value It's not a descriptor
self._member_names.append(key)
self._last_values.append(value)
super().__setitem__(key, value)

The module will consider more comprehensively .

Each member has a name attribute and a value attribute
In the above code ,Color.red The value obtained is 1. and eumu Module , Defined enumeration class , Each member has a name and attribute value ; And if you are careful, you will find Color.red yes Color Example . How does this happen .

Or use metaclasses to complete , In the of metaclasses new To realize , The specific idea is , First create the target class , Then create the same class for each member , Re pass setattr Add subsequent classes as attributes to the target class , The pseudocode is as follows :

def __new__(metacls, cls, bases, classdict):
__new__ = cls.__new__
# Create enumeration class
enum_class = super().__new__()
# Every member is cls An example of , adopt setattr Inject into the target class
for name, value in cls.members.items():
member = super().__new__()
member.name = name
member.value = value
setattr(enum_class, name, member)
return enum_class

Take a look at the next runnable demo:

class _Dict(dict):
def __init__(self):
super().__init__()
self._member_names = []
def __setitem__(self, key, value):
if key in self:
raise TypeError('Attempted to reuse key: %r' % key)
if not key.startswith("_"):
self._member_names.append(key)
super().__setitem__(key, value)
class MyMeta(type):
@classmethod
def __prepare__(metacls, name, bases):
d = _Dict()
return d
def __new__(metacls, cls, bases, classdict):
__new__ = bases[0].__new__ if bases else object.__new__
# Create enumeration class
enum_class = super().__new__(metacls, cls, bases, classdict)
# Create members
for member_name in classdict._member_names:
value = classdict[member_name]
enum_member = __new__(enum_class)
enum_member.name = member_name
enum_member.value = value
setattr(enum_class, member_name, enum_member)
return enum_class
class MyEnum(metaclass=MyMeta):
pass
class Color(MyEnum):
red = 1
blue = 2
def __str__(self):
return "%s.%s" % (self.__class__.__name__, self.name)
print(Color.red) # Color.red
print(Color.red.name) # red
print(Color.red.value) # 1

enum The implementation idea of the module in making each member have the attribute of name and value is the same ( I won't post the code ).EnumMeta.__new__ Is the focus of this module , Almost all enumeration features are implemented in this function .

When the member values are the same , The second member is the alias of the first member
From this section, you will no longer use the description of your own implemented classes , But by disassembling enum Module code to illustrate its implementation , We can know from the use characteristics of the module , If the member values are the same , The latter will be an alias of the former :

from enum import Enum
class Color(Enum):
red = 1
_red = 1
print(Color.red is Color._red) # True

From this we can know ,red and _red It's the same thing . How can this be achieved ?

Metaclasses are created for enumeration classes

member_map

Property to store the mapping relationship between member names and members , If you find that the value of the created member is already in the mapping relationship , Will be replaced by objects in the mapping table :

class EnumMeta(type):
def __new__(metacls, cls, bases, classdict):
...
# create our new Enum type
enum_class = super().__new__(metacls, cls, bases, classdict)
enum_class._member_names_ = [] # names in definition order
enum_class._member_map_ = OrderedDict() # name->value map
for member_name in classdict._member_names:
enum_member = __new__(enum_class)
# If another member with the same value was already defined, the
# new member becomes an alias to the existing one.
for name, canonical_member in enum_class._member_map_.items():
if canonical_member._value_ == enum_member._value_:
enum_member = canonical_member # replace
break
else:
# Aliases don't appear in member names (only in __members__).
enum_class._member_names_.append(member_name) # New blood , Add to _member_names_ in
enum_class._member_map_[member_name] = enum_member
...

In terms of code , Even if the member values are the same , Or will you create objects for them first , What you create later will soon be garbage collected ( I think there is room for optimization ). Through and with

member_map

Compare the mapping table , Replace the subsequent , But the names of both members will be

member_map

in , As in the example red and _red All in this dictionary , But they point to the same object .

attribute

member_names

Only the first one will be recorded , This will be related to the iteration of enumeration .

You can get members by member values

print(Color['red']) # Color.red Get members by member name
print(Color(1)) # Color.red Get members by member values

The members in the enumeration class are all in singleton mode , The enumeration class created by metaclass also maintains the mapping relationship from value to member

value2member_map

:

class EnumMeta(type):
def __new__(metacls, cls, bases, classdict):
...
# create our new Enum type
enum_class = super().__new__(metacls, cls, bases, classdict)
enum_class._value2member_map_ = {}
for member_name in classdict._member_names:
value = enum_members[member_name]
enum_member = __new__(enum_class)
enum_class._value2member_map_[value] = enum_member
...

And then in Enum Of new Just return to the singleton :

class Enum(metaclass=EnumMeta):
def __new__(cls, value):
if type(value) is cls:
return value
# Try from _value2member_map_ obtain
try:
if value in cls._value2member_map_:
return cls._value2member_map_[value]
except TypeError:
# from _member_map_ Map get
for member in cls._member_map_.values():
if member._value_ == value:
return member
raise ValueError("%r is not a valid %s" % (value, cls.__name__))

Iterate through members
Enumeration classes support iteratively traversing members , In defined order , If there are members with duplicate values , Get only the first member of the duplicate . For duplicate member values, only the first member is obtained , Exact attribute

member_names

Only the first one will be recorded :

class Enum(metaclass=EnumMeta):
def __iter__(cls):
return (cls._member_map_[name] for name in cls._member_names_)

summary
enum The implementation idea of the core features of the module is like this , Almost all of them are realized through meta dark magic . For members, you can't compare the size, but you can do equivalent comparison . Instead, there is no need to say , This is actually inherited from object That's it , There is nothing extra to do “ characteristic ” 了 .
All in all ,enum Modules are relatively independent , And the amount of code is not much , For those who want to know metaclass programming, you can read , Textbook teaching , There are also singleton patterns , Worth reading .

The above is all the content shared this time , Want to know more python Welcome to official account :Python Programming learning circle , send out “J” Free access to , Daily dry goods sharing


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved