在python中为特定示例提供默认值的推荐方法是什么?

g6baxovj  于 2021-09-08  发布在  Java
关注(0)|答案(2)|浏览(227)

我希望根据对象的初始化参数,使用某些默认属性值初始化某些示例。我正在考虑使用嵌套字典作为类属性,但由于某些原因,它感觉很复杂。对于这种情况是否有最佳实践?

class Shape:

    metadata = {
        3: {"names": ["Triangle", "Triforce"], "color": "sage"},
        4: {"names": ["Square", "Box", "Cube"], "color": "dusty rose"},
        12: {"names": ["Dodecagon", "Crude circle"], "color": "gold"}
    }

    colors = ["red", "orange", "yellow", "green", "blue", "indigo", "violet"]

    def __init__(self, sides, *names):
        # All instances will certainly have the same attributes
        self.sides = sides
        self.names = list(names)
        # Most attributes will have automatically generated values based on
        # the init parameters
        self.color = self.colors[sides % 7]
        self.names += [str(sides) + "-gon"]
        # But a few will have commonly recognized values which I'd like to set
        # manually.
        # This is the part I'm not sure how to handle
        data = __class__.metadata.get(sides)
        if data is not None:
            self.names += data["names"]
            self.color = data["color"]

我可以在创建对象后添加自定义值,但如果我创建了另一个具有相同初始化参数的对象,它将不会保留这些自定义值(即,我希望我的所有形状(3)对象都具有名称“三角形”)。

7xzttuei

7xzttuei1#

我认为这感觉很复杂的原因是因为你的形体课试图一次做太多的事情。理想情况下,类应该对程序行为的一部分负责(这是单一责任原则)。
我建议您对代码进行两项主要更改。

不要让shape类负责创建自己

一个形状实际上不需要知道所有其他可能的形状类型,也不需要知道决定它是哪种形状所需的规则。此代码可以抽象为另一个类,因此形状可以集中于包含边、形状和颜色。我建议使用类似于工厂模式的方法来实现这一点(https://en.wikipedia.org/wiki/factory_method_pattern).

考虑使用多态性

如果你计划只将形状作为边名称和颜色的容器,那么你当前的课程将会很好。但是,如果您想添加根据形状类型而变化的功能(例如,如果您想计算它的面积),您将在shapes类中遇到一些复杂的逻辑,这将意味着它再次做了太多的事情。

例如:

class Shape:
    def __init__(self, sides, color, *names):
        self.sides = sides
        self.color = color
        self.names = names

    def __str__(self):
       return "Sides: {}\nNames:{}\nColor: {}\n".format(self.sides, self.names, self.color)

class Triangle(Shape):
    def __init__(self, color, *names):
        super().__init__(3, color, *names)

class Square(Shape):
    def __init__(self, color, *names):
        super().__init__(4, color, *names)

class BlueShapeFactory:
    def createShapes(sides):
        if sides == 3:
            return Triangle("Blue", "PointyBoy", "Triangle")
        elif sides == 4:
            return Square("Blue", "Uncool", "Square")
        else:
            return Shape(sides, "Blue", str(sides) + "-o-gon")

class DefaultShapeFactory:
    def createShapes(sides):
        if sides == 3:
            return Triangle("green", "Triforce", "Triangle")
        elif sides == 4:
            return Square("blue", "Box", "Cube", "Square")
        else:
            return Shape(sides, "purple", str(sides) + "-o-gon")

print("Blueshapes:\n")
print(BlueShapeFactory.createShapes(3))
print(BlueShapeFactory.createShapes(4))
print(BlueShapeFactory.createShapes(42))

print("Your shapes:\n")
print(DefaultShapeFactory.createShapes(3))
print(DefaultShapeFactory.createShapes(4))
print(BlueShapeFactory.createShapes(42))
k5ifujac

k5ifujac2#

我找到了一个解决方案:使用flyweight设计模式。
使用此设计模式,对于每个初始化参数,一个示例只示例化一次,如果使用相同的init参数再次尝试构造,则引用该示例。这类似于某些不可变python内置的对象缓存。
不要将默认属性值保存在类定义中,只需在示例化后设置这些值即可。任何具有相同初始化参数的未来对象都将保留这些自定义值,因为它将引用第一个对象。

class Shape:

    _instances = {}

    colors = ["red", "orange", "yellow", "green", "blue", "indigo", "violet"]

    def __new__(cls, sides, *names):
        # Retrieve the Shape instance if previously instantiated
        self = cls._instances.get(sides)
        # If it has not yet been instantiated...
        if self is None:
            # Save this Shape instance
            self = cls._instances[sides] = object.__new__(Shape)
            # Initialize here for efficiency (because __init__ gets called
            # automatically, even when returning a previously instantiated
            # object)
            self.sides = sides
            self.color = cls.colors[sides % 7]
            self.names = list(names)
            self.names += [str(sides) + "-gon"]
        return self

triangle = Shape(3)
triangle.names += ["triangle", "triforce"]
triangle.color = "sage"

square = Shape(4)
square.names += ["Square", "Box", "Cube"]
square.color = "dust rose"

dodecagon = Shape(12)
dodecagon.names += ["Dodecagon", "Crude circle"]
dodecagon.color = "gold"

通常,这种模式应该用于不可变对象,因为修改一个对象的属性会导致在引用该对象的任何地方都进行更改,这可能会导致意外的结果。然而,在这种情况下,这是可取的行为。

相关问题