为什么我应该使用__prepare__方法来获得一个班级'命名空间?

时间:2017-05-06 14:21:15

标签: python python-3.x metaclass

注意 此问题与Python 3 Enum数据类型无关,它只是我使用的示例。 < / p>

使用PEP 3115 Python 3将__prepare__ 1 方法添加到type,以便在创建类时允许使用自定义命名空间。例如,新的Enum数据类型使用__prepare__来返回私有_EnumDict的实例,以用作新的Enum类&#39;命名空间。

但是,我已经看到了几个关于EnumMeta的SO 2 被子类化的例子,在元类__new__方法中为类创建了一个新的命名空间,而不是调用获取新命名空间的__prepare__方法,而是使用type(clsdict)()。这样做有风险吗?

1 __prepare__的签名:

@classmethod
def __prepare__(metacls, cls, bases, **kwds):

__new__

def __new__(metacls, cls, bases, clsdict, **kwds):

2 使用type(clsdict)的示例:

来自this answer

class CountryCodeMeta(enum.EnumMeta):
    def __new__(metacls, cls, bases, classdict):
        data = classdict['data']
        names = [(country['alpha-2'], int(country['country-code'])) for country in data]

  -->   temp = type(classdict)()
        for name, value in names:
            temp[name] = value

        excluded = set(temp) | set(('data',))
        temp.update(item for item in classdict.items() if item[0] not in excluded)

        return super(CountryCodeMeta, metacls).__new__(metacls, cls, bases, temp)

1 个答案:

答案 0 :(得分:8)

是的,存在风险。

通过调用__prepare__而不是type(clsdict)()来获取新命名空间至少有两个原因:

  • 在Python 2 clsdict上运行时dict__prepare__,原始__prepare__从未运行过(__prepare__仅限Python 3) - 换句话说,如果type(clsdict)()正在返回除正常字典之外的内容,__prepare__将无法获得它。

  • 使用clsdict时,type(clsdict)()上由__prepare__设置的所有属性都不会设置;即如果clsdict.spam = 'eggs' type(clsdict)() spam,则__prepare__()将不具有type(clsdict)()属性。请注意,这些属性位于名称空间本身以供元类使用,并且在名称空间中不可见。

总结一下:有充分的理由使用 //Binary search method. public static void BinarySearch<T>(T[] data) { Binary_Search(data, Console.ReadLine(), Comparer<T>.Default); } 来获取正确的类字典,而 //Binary search algorithm public static int Binary_Search<T>(T[] data, T searchFor, IComparer<T>comparer) { int high, low, mid; high = data.Length - 1; low = 0; //if the first element of the array is what I'm looking for then return that element. if (data[0].Equals(searchFor)) return 0; //else if highest element in the array is the item im looking for then return the element. else if (data[high].Equals(searchFor)) return high; else { //While low point is lower than or equal with high point set the mid point to be in the middle of the highest and lowers point. while (low <= high) { mid = (high + low) / 2; //Compare mid point to the item searched, if the difference is 0 it means the item is there, return the element. if (comparer.Compare(data[mid], searchFor) == 0) return mid; //Else if the difference between mid point and searched item is bigger than 0, the searched item must be lower than mid point, //set the new high point to be current mid -1; else if (comparer.Compare(data[mid], searchFor) > 0) high = mid - 1; //Else (Current mid point is lower than the searched item) set the new low point to be the current mid point +1. else low = mid + 1; } return -1; } } 快捷方式则没有。