在一个很棒的系统上,该系统使用类似枚举的方式替换Django选择(http://musings.tinbrain.net/blog/2017/may/15/alternative-enum-choices/),我有一个项目使用带有自定义元类的类,该类允许我执行list(MyChoices)
(在类本身)以获取所有枚举选择的列表。代码的相关部分看起来像这样:
class MetaChoices(type):
@classmethod
def __prepare__(mcs, name, bases, **kwargs):
return OrderedDict()
def __new__(mcs, name, bases, attrs):
_choices = OrderedDict()
for attr_name, value in list(attrs.items()):
...do things...
return type.__new__(mcs, name, bases, dict(attrs))
def __iter__(cls):
return iter(cls._choices.items())
class Choices(metaclass=MetaChoices):
pass
class IceCreamFlavor(Choices):
STRAWBERRY = ('strawberry', 'Fruity')
CHOCOLATE = 'chocolate'
list(IceCreamFlavor)
# [('strawberry', 'Fruity'), ('chocolate', Chocolate')
代码运行良好已有一段时间了,但是现在我已经打开了键入功能(在这种情况下,使用PyCharm的类型检查器,但也在寻找常规解决方案),尽管IceCreamFlavor
仍未标记为可迭代它是从其元类将cls
定义为具有__iter__
方法的类派生的。有谁知道一个可以证明Choices
类本身是可迭代的解决方案吗?
答案 0 :(得分:1)
我建议将代码修正为MyPy正确的(通过先添加注释文件* .pyi的Pytype进行检查比较容易)。也许deleteRange()
中当前的键入问题可能导致检查器中忽略了元类方法。如果它没有帮助,并且简化示例也失败了,那么可以真正报告它。这不是一个大问题,因为它可以由代码中的抽象基类来提示。
您唯一的键入问题是方法/*
*/
package de.test.stackoverflow;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.lang.StringUtils;
public class Intervals {
public static final class Range {
final int from;
final int to;
public Range(int from, int to) {
this.from = from;
this.to = to;
}
public int getFrom() {
return from;
}
public int getTo() {
return to;
}
@Override
public String toString() {
return String.format("[%d, %d)", from, to);
}
}
final SortedSet<Integer> intervalBoundaries = new java.util.TreeSet<>();
public List<Range> getRanges() {
final List<Range> res = new java.util.ArrayList<>();
if (intervalBoundaries.size() > 1) {
final List<Integer> tmpBounds = new java.util.ArrayList<>(intervalBoundaries);
for (int i = 0; i < tmpBounds.size() - 1; i++) {
res.add(new Range(tmpBounds.get(i), tmpBounds.get(i + 1)));
}
}
return res;
}
public void insertBoundary(Integer bound) {
// duplicates in sets are automatically removed
intervalBoundaries.add(bound);
}
public void insertRange(Integer from, Integer to) {
intervalBoundaries.add(from);
intervalBoundaries.add(to);
}
public void insertRange(Range x) {
insertRange(x.getFrom(), x.getTo());
}
public void deleteRange(Range x) {
final Collection<Integer> boundsToDelete = new java.util.LinkedHashSet<>();
for (Integer intervalBoundary : intervalBoundaries) {
if (intervalBoundary >= x.getFrom() && intervalBoundary < x.getTo()) {
boundsToDelete.add(intervalBoundary);
}
}
intervalBoundaries.removeAll(boundsToDelete);
if (x.getTo() < intervalBoundaries.last()) {
insertBoundary(x.getTo());
}
if (x.getFrom() > intervalBoundaries.last()) {
insertBoundary(x.getFrom());
}
}
public static void main(String[] args) {
Intervals i = new Intervals();
i.insertRange(new Range(1, 4));
i.insertRange(new Range(4, 9));
i.insertRange(new Range(9, 12));
System.out.println("Given collection: " + StringUtils.join(i.getRanges(), ", "));
final Range insertRange = new Range(5, 8);
System.out.println("insert: " + insertRange);
i.insertRange(insertRange);
System.out.println("Result: " + StringUtils.join(i.getRanges(), ", "));
final Range deleteRange = new Range(4, 5);
System.out.println("delete: " + deleteRange);
i.deleteRange(deleteRange);
System.out.println("Result: " + StringUtils.join(i.getRanges(), ", "));
final Range deleteRange2 = new Range(3, 9);
System.out.println("delete: " + deleteRange2);
i.deleteRange(deleteRange2);
System.out.println("Result: " + StringUtils.join(i.getRanges(), ", "));
final Range deleteRange3 = new Range(2, 15);
System.out.println("delete: " + deleteRange3);
i.deleteRange(deleteRange3);
System.out.println("Result: " + StringUtils.join(i.getRanges(), ", "));
}
}
中的属性__iter__
对于检查者而言似乎是未定义的,因为它不是由__iter__
透明地分配的。可以通过添加一行来进行注释:
_choices
它对Pytype完全有效,并且带有注释的MyPY当然也对其进行了检查。
如果没有帮助,则可以通过抽象基类声明可迭代的接口:
attrs['_choices'] = ...
报告此问题的可能的简化示例是:
class MetaChoices(type):
_choices = None # type: dict # written as comment for Python >= 3.5
# _choices: dict # use this line if only Python >= 3.6
我在原始文章中报告了另一个与该问题无关的小错误。