简体   繁体   English

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

[英]What's the recommended way to provide default values to specific instances in Python?

I'd like certain instances to initialize with certain default attribute values depending on the initialization parameters of the object.我希望某些实例根据对象的初始化参数使用某些默认属性值进行初始化。 I'm considering using a nested dictionary as a class attribute, but it feels convoluted for some reason.我正在考虑使用嵌套字典作为类属性,但由于某种原因感觉很复杂。 Is there a best practice for this type of situation?这种情况有最佳实践吗?

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"]

I could add the custom values after creating the objects, but if I ever create another object with the same initialization parameters, it won't retain those custom values (ie I want all my Shape(3) objects to posses the name "Triangle").我可以创建对象添加自定义值,但是如果我创建另一个具有相同初始化参数的对象,它不会保留这些自定义值(即我希望我的所有 Shape(3) 对象都具有名称“三角形” )。

I think the reason it feels complicated is because your Shape class is trying to do too many things at once.我认为它感觉复杂的原因是你的 Shape 类试图一次做太多的事情。 Ideally, a class should be responsible for a single part of your programs behavior (this is the Single Responsibility Principle).理想情况下,类应该负责程序行为的单个部分(这是单一职责原则)。

I'd recommend two main changes to your code.我建议对您的代码进行两项主要更改。

Don't make the Shape class responsible for creating itself不要让 Shape 类负责创建自己

A shape doesn't really need to know about all other possible kinds of shapes, or the rules required for deciding which kind of shape it is.一个形状并不真正需要知道所有其他可能的形状,或者决定它是哪种形状所需的规则。 This code can be abstracted out into another class, so the shape can focus on containing sides, shapes and colours.这段代码可以抽象成另一个类,所以形状可以专注于包含边、形状和颜色。 I'd recommend using something like the Factory Pattern for this ( https://en.wikipedia.org/wiki/Factory_method_pattern ).我建议为此使用工厂模式( https://en.wikipedia.org/wiki/Factory_method_pattern )。

Consider using polymorphism考虑使用多态

If you plan on only ever having shapes be containers for sides names and colours, your current class will work fine.如果您打算只将形状作为边名称和颜色的容器,那么您当前的类将可以正常工作。 However, if you ever want to add functionality that changes depending on the kind of shape (if you wanted to calculate it's area, say), you'll wind up with some complicated logic in your shapes class which will mean it's back doing too many things again.但是,如果您想添加根据形状类型而变化的功能(例如,如果您想计算它的面积),那么您的形状类中会出现一些复杂的逻辑,这意味着它又回来做太多了事情又来了。

Example:例子:

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))

I found a solution: Use The Flyweight design pattern .我找到了一个解决方案:使用享元设计模式

Using this design pattern, for each initialization parameter, an instance is only instantiated once, and referenced if construction is attempted again with the same init parameter(s).使用这种设计模式,对于每个初始化参数,一个实例只被实例化一次,如果使用相同的初始化参数再次尝试构造,则引用该实例。 This is similar to object caching of certain immutable Python built-ins.这类似于某些不可变 Python 内置函数的对象缓存。

Rather than saving default attribute values inside the class definition, simply set those values after instantiation.无需在类定义中保存默认属性值,只需在实例化后设置这些值即可。 Any future object with the same initialization parameters will retain those custom values because it will reference the first object.任何具有相同初始化参数的未来对象都将保留这些自定义值,因为它将引用第一个对象。

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"

Usually, this pattern should be used for immutable objects, because modifying attributes of one object causes that change to be made everywhere that object is referenced, which could cause unexpected results.通常,这种模式应该用于不可变对象,因为修改一个对象的属性会导致在引用该对象的任何地方都进行更改,这可能会导致意外结果。 However, in this case, that is desirable behavior.然而,在这种情况下,这是可取的行为。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 单元测试Python GUI应用程序的推荐方法是什么? - What's the recommended way to unittest Python GUI applications? 使用 MLRun 组织代码的推荐方法是什么? - What's the recommended way to organize code with MLRun? 推荐的电话号码存储方式是什么? - What's the recommended way for storing a phone number? 在 pypi 中重命名项目的推荐方法是什么? - What's the recommended way of renaming a project in pypi? 大多数惯用的方式在python中提供默认值? - Most idiomatic way to provide default value in python? 什么是外部库的Python推荐名称? - What is the Python's recommended name for the external libraries? 在python中为非空集合返回布尔值的推荐方法是什么? - What's the recommended way to return a boolean for a collection being non-empty in python? 根据python版本导入`unittest`或`unittest2`的推荐方法是什么 - What's the recommended way to import `unittest` or `unittest2` depending on python version 在Python中同时删除参数顺序和提供默认值的最安全方法 - Safest way to remove argument order and provide default values at the same time in Python 序列化`tf.Module`s的推荐方法是什么? - What's the recommended way to serialize `tf.Module`s?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM