简体   繁体   English

实例化后向对象添加标志的pythonic方法是什么?

[英]What is the pythonic way to add a flag to an object after it has been instantiated?

Imagine a factory that produces bottles and at the end, a label is attached to each bottle, indicating its contents.想象一家生产瓶子的工厂,最后,每个瓶子都贴有标签,标明其内容。

So we have objects of class Bottle, and a function that attaches an object of type Label to each Bottle.所以我们有Bottle类的对象,以及一个将Label类型的对象附加到每个Bottle的函数。

The issue then is that the label cannot be given to bottle in its __init__ function, as the label is created after the bottle.那么问题是标签不能在它的__init__函数中被赋予瓶子,因为标签是在瓶子之后创建的。 We could of course just add a flag that equals None, and mutate it later, but then we have two issues: a) mutation and b) we cannot differentiate between the situations where the label has not yet been put on and the situation where the label has been put on with a value equalling None.我们当然可以只添加一个等于 None 的标志,然后再对其进行变异,但是我们有两个问题:a) 变异和 b) 我们无法区分尚未贴上标签的情况和已使用等于 None 的值放置标签。

What is the pythonic way to handle this?处理这个的pythonic方法是什么?

My solution is below, which looks horrible!我的解决方案如下,看起来很糟糕! What else can I do?我还可以做些什么?

class BottleWithLabel(Bottle):
    def __init__(bottle, label)
        self.bottle = bottle
        self.label = label
   def __getattribute__(attr):
       if attr not in ['label']:
            return getattr(self.bottle, attr)
       return object.__getattribute__(self, attr)

# apply label
bottle = BottleWitLabel(bottle, label)
# get label
bottle.label
# check if bottle has undergone the labelling process
isinstance(bottle, BottleWithLabel)

I think the cleanest way for the use-case you described is to use a sentinel value , ie an object that indicates you don't have a label.我认为对于您描述的用例,最简洁的方法是使用哨兵值,即表示您没有标签的对象。 In that way, your label may be None :这样,您的标签可能是None

class Unlabelled:
    def __init__(self): raise Exception('Should not be instantiated')


class Bottle:
    def __init__(self):
        self.label = Unlabelled  # Note: not an instance

    def is_labelled(self):  # Alternative method usage
        return self.label is not Unlabelled


b = Bottle()
if b.label is Unlabelled:  # Could also be b.label == Unlabelled
    b.label = None
assert b.is_labelled()

Being mutable or immutable is perpendicular to this implementation: you can add a method to return a copy of the object every time you label it, for example.可变或不可变与此实现垂直:例如,您可以添加一个方法以在每次标记对象时返回对象的副本。

As for the "pythonicity" of this solution, it is so common that there is a PEP about that.至于这个解决方案的“pythonicity”,它是如此普遍,以至于有一个关于它的PEP You can see different implementations (including simpler ones) on that PEP as well.您还可以在该 PEP 上看到不同的实现(包括更简单的实现)。

It might be worth adding more details about the use cases for these classes to get a more specific answer to your question.可能值得添加有关这些类的用例的更多详细信息,以获得对您的问题的更具体的答案。 In general, I would probably just use label=None in __init__ but if you are really opposed to that, there are other ways.一般来说,我可能只会在__init__中使用label=None ,但如果你真的反对,还有其他方法。 Python's hasattr function will tell you if the bottle has a label attribute yet Python 的hasattr函数会告诉你瓶子是否有标签属性

def hasLabel(bottle : Bottle) -> bool:
    return hasattr(bottle, "label")

Then labeling the bottle is as simple as然后给瓶子贴标签很简单

bottle1.label = "my Label"
bottle2.label = None # this will still register with hasattr as "having a label"

It sounds to me like you have two different types of thing here - a bottle which hasn't gone through the labelling process, and a bottle which has (though it may have had no label attached to it).在我看来,你这里有两种不同类型的东西——一个没有经过标签过程的瓶子,一个有(尽管它可能没有贴上标签)的瓶子。 Especially if you want to avoid mutation, your solution which makes BottleWithLabel a separate type is the right way to go, but your solution which delegates attribute accesses to the bottle object is not Pythonic, because "explicit is better than implicit".特别是如果您想避免突变,使BottleWithLabel成为单独类型的解决方案是正确的方法,但是您将属性访问委托给bottle对象的解决方案不是 Pythonic,因为“显式优于隐式”。 I would simply do this:我会这样做:

from collections import namedtuple
LabelledBottle = namedtuple('label bottle')

bottle = Bottle('freshly pressed apple juice')
labelled_bottle = LabelledBottle('ACME brand apple juice', bottle)

print('A bottle of', labelled_bottle.bottle.contents)
# A bottle of freshly pressed apple juice

Note that this way, you have to explicitly access labelled_bottle.bottle.contents instead of simply labelled_bottle.contents .请注意,这种方式,您必须显式访问labelled_bottle.bottle.contents而不是简单地labelled_bottle.contents This means you can't use a Bottle and a LabelledBottle interchangeably, so your functions will need to decide whether they accept bottles or labelled bottles.这意味着您不能互换使用BottleLabelledBottle ,因此您的函数需要决定它们是接受瓶子还是带标签的瓶子。

That's a good thing!这是好事! When you're writing a function, you should know whether the bottles it accepts have gone through the labelling process or not.当你写一个函数时,你应该知道它接受的瓶子是否经过了标签过程。 But if you really need the same part of your code to handle both types, consider something like this, to make explicit that this function accepts either type:但是,如果您确实需要代码的同一部分来处理这两种类型,请考虑这样的事情,以明确该函数接受任何一种类型:

def handle_bottle(bottle):
    is_labelled = isinstance(bottle, LabelledBottle)
    label, bottle = bottle if is_labelled else (None, bottle)
    if is_labelled:
        # label might still be None here
        print('A bottle of', bottle.contents, 'with a label of', label)
    else:
        print('A bottle of', bottle.contents, 'with no label')

If you want a way to add a label without any mutation, you can write a method addLabel that creates a new Bottle with a label.如果您想要一种添加标签而不进行任何突变的方法,您可以编写一个方法addLabel来创建一个带有标签的新瓶。 Optionally, you could write a hasLabel method that checks whether the bottle has a label.或者,您可以编写一个hasLabel方法来检查瓶子是否有标签。

class Bottle:
    """A bottle. The optional argument specifies a label."""

    def __init__(self, label=None):
        self.label = label

    def addLabel(self, label):
        """Make a new bottle with a label added."""
        return Bottle(label)

    def hasLabel(self):
        """Return True if the bottle has been labelled."""
        return self.label is not None


label = 'Water'
# apply label
bottle = Bottle(label)
# get label
print(bottle.label)
# check if bottle has undergone the labelling process
print(bottle.label is not None)

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM