【问题标题】:Strange behavior with generator\iterator生成器\迭代器的奇怪行为
【发布时间】:2016-01-04 19:20:01
【问题描述】:

我为一些 OOP 练习创建的迭代器有问题。

这是有问题的生成器:

def shapeIterator(listOfShapes):
    print("Generator...")
    print(listOfShapes)
    listOfShapessoretedbyArea = shape.sortedByArea(listOfShapes)
    for shapes in listOfShapessoretedbyArea:
        yield str(shapes)

shape.sortedByArea(listOfShapes) 是一个静态方法,需要一个参数,一个列表,按计算区域排序,返回给调用者。 这个方法在这个主函数中完美运行:

if __name__ == '__main__':
    rect = rectangle(20, 5)
    squa = square(2)
    tri = equiTria(2, 5)
    circ = circle(2)
    pent = pentagon(5)
    hexa = hexagon(3)
    listOfShapes = [rect, squa, hexa, tri, circ, pent]
    listOfShapessoretedbyArea = sorted(listOfShapes, key=lambda x: x.calculate_area())
    listOfShapessoretedbyPeri = sorted(listOfShapes, key=lambda x: x.calculate_perimeter())
    listOfShapessoretedbyArea2 = shape.sortedByArea(listOfShapes)
    listOfShapessoretedbyPeri2 = shape.sortedByPerim(listOfShapes)
    iterator = shapeIterator(listOfShapes)
    for i in range(6):
        sleep(1)
        value = next(iterator)
        print(value)
    print("NOT SORTED")
    for shape in listOfShapes:
        print(str(shape))
    print("\nSORTED BY AREA")
    for shape in listOfShapessoretedbyArea:
        print(str(shape))
    print("\nSORTED BY PERIMETER")
    for shape in listOfShapessoretedbyPeri:
        print(str(shape))
    print("\nSORTED BY AREA v2")
    for shape in listOfShapessoretedbyArea2:
        print(str(shape))
    print("\nSORTED BY PERIMETER v2")
    for shape in listOfShapessoretedbyPeri2:
        print(str(shape))

但是当我移动这部分时:

iterator = shapeIterator(listOfShapes)
for i in range(6):
    sleep(1)
    value = next(iterator)
    print(value)

在正文的末尾,像这样:

if __name__ == '__main__':
    rect = rectangle(20, 5)
    squa = square(2)
    tri = equiTria(2, 5)
    circ = circle(2)
    pent = pentagon(5)
    hexa = hexagon(3)
    listOfShapes = [rect, squa, hexa, tri, circ, pent]
    listOfShapessoretedbyArea = sorted(listOfShapes, key=lambda x: x.calculate_area())
    listOfShapessoretedbyPeri = sorted(listOfShapes, key=lambda x: x.calculate_perimeter())
    listOfShapessoretedbyArea2 = shape.sortedByArea(listOfShapes)
    listOfShapessoretedbyPeri2 = shape.sortedByPerim(listOfShapes)
    print("NOT SORTED")
    for shape in listOfShapes:
        print(str(shape))
    print("\nSORTED BY AREA")
    for shape in listOfShapessoretedbyArea:
        print(str(shape))
    print("\nSORTED BY PERIMETER")
    for shape in listOfShapessoretedbyPeri:
        print(str(shape))
    print("\nSORTED BY AREA v2")
    for shape in listOfShapessoretedbyArea2:
        print(str(shape))
    print("\nSORTED BY PERIMETER v2")
    for shape in listOfShapessoretedbyPeri2:
        print(str(shape))
    iterator = shapeIterator(listOfShapes)
    for i in range(6):
        sleep(1)
        value = next(iterator)
        print(value)

我收到了这个错误:

TypeError: sortedByArea() takes 1 positional argument but 2 were given

这很奇怪。尝试做一些简单的调试,我打印了在第二种情况下通过函数 sortedByArea() 传递的参数,并且我有效地得到了两个参数。一个是每个语句最后打印的 to 字符串值,第二个是列表本身。

每个语句的最后一个字符串值都引用了这个:

for shape in listOfShapessoretedbyPeri2:
    print(str(shape))

我还尝试更改列表的值,实际上“连接”到传递给 shapeIterator 函数的参数的值是最后打印的字符串。

如果需要,这里是主 .py 中使用的类和导入:

from math import pi
from math import sqrt
from time import sleep


class shape():

    def calculate_area():
        pass

    def calculate_perimeter():
        pass

    def ltarea(self, other):
        return self.calculate_area() < other.calculate_area()

    def ltperim(self, other):
        return self.calculate_perimeter() < other.calculate_perimeter()

    def sortedByArea(shapes):
        return sorted(shapes, key=lambda x: x.calculate_area())

    def sortedByPerim(shapes):
        return sorted(shapes, key=lambda x: x.calculate_perimeter())

    def nametype(self):
        return "shape"

    def __str__(self):
        return "{0}, area: {1}, perim: {2}".format(self.nametype(),
                                               self.calculate_area(),
                                               self.calculate_perimeter())


class rectangle(shape):
    def __init__(self, side1, side2):
        self.__side1 = side1
        self.__side2 = side2

    def calculate_area(self):
        return self.__side1 * self.__side2

    def calculate_perimeter(self):
        return (self.__side1 * 2) + (self.__side2 * 2)

    def nametype(self):
        return "rectangle"


class square(rectangle):
    def __init__(self, side):
        self._rectangle__side1 = side
        self._rectangle__side2 = side

    def nametype(self):
        return "square"


class equiTria(shape):
    def __init__(self, side, height):
        self.__side = side

    def calculate_area(self):
        self.__height = self.calculate_perimeter() / (2 * sqrt(3))
        return (self.__side * self.__height)/2

    def calculate_perimeter(self):
        return self.__side * 3

    def nametype(self):
        return "equiTria"


class circle(shape):
    def __init__(self, radius):
        self.__radius = radius

    def calculate_area(self):
        return pi * pow(self.__radius, 2)

    def calculate_perimeter(self):
        return 2 * pi * self.__radius

    def nametype(self):
        return "circle"


class pentagon(shape):
    def __init__(self, side):
        self.__side = side
        self.__apothem = side * 0.688

    def calculate_perimeter(self):
        return self.__side * 5

    def calculate_area(self):
        return (self.calculate_perimeter() * self.__apothem) / 2

    def nametype(self):
        return "pentagon"


class hexagon(shape):
    def __init__(self, side):
        self.__side = side

    def calculate_area(self):
        self.__apothem = self.__side * 0.866
        return (self.calculate_perimeter() * self.__apothem) / 2

    def calculate_perimeter(self):
        return self.__side * 6

    def nametype(self):
        return "hexagon"


def shapeIterator(listOfShapes):
    print("Generator...")
    print(listOfShapes)
    listOfShapessoretedbyArea = shape.sortedByArea(listOfShapes)
    for shapes in listOfShapessoretedbyArea:
        yield str(shapes)

【问题讨论】:

  • 除了重用shape 的问题之外,像sortedByArea 这样的静态方法应该用@staticmethod 装饰器装饰或完全移出类。这在 Python 2 中更为重要,但仍应在 Python 3 中完成。

标签: python iterator generator


【解决方案1】:

你在循环中重新绑定shape,所以它不再是类,而是一个实例。

例如,上面你使用的生成器:

for shape in listOfShapessoretedbyPeri2:
    print(str(shape))

__main__ 部分中的变量仍然是全局变量,因此替换了生成器使用的类。

您的选择是:

  1. 为循环变量使用不同的名称;以ashape 为例。
  2. 为类使用不同的名称。 Python 样式指南建议使用 CamelCase 作为类名,因此在这里将其重命名为 Shape 会很好。
  3. if __name__ == '__main__':块下的所有代码放在一个函数中,这样循环目标之类的变量名就变成了locals

就个人而言,我会同时实现 2 3;避免污染全局命名空间总是一个好主意,遵循几乎普遍采用的 Python 样式指南也是如此;这有助于避免将来发生此类错误。

另外,如果sortedByArea 是一个静态方法,至少要使用@staticmethod 装饰器。这样即使在实例上它仍然可以用作静态方法:

class Shape:
    # ...

    @staticmethod
    def sortedByArea(shapes):
        return sorted(shapes, key=lambda x: x.calculate_area())

    @staticmethod
    def sortedByPerim(shapes):
        return sorted(shapes, key=lambda x: x.calculate_perimeter())

【讨论】:

    【解决方案2】:

    您重用了shape 变量,一次用于shape 类,一次用于所有for shape in 循环中的循环变量。

    【讨论】:

      猜你喜欢
      • 2016-04-22
      • 1970-01-01
      • 1970-01-01
      • 2013-11-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-16
      • 2020-02-29
      相关资源
      最近更新 更多