【问题标题】:if condition for multiple methods in a class一个类中多个方法的 if 条件
【发布时间】:2015-06-10 23:20:10
【问题描述】:

我正在使用 python 编写代码,以便更好地了解使用类时的良好做法。我为几个类写了一些方法;狗和人。我介绍了一种名为 die 的狗的新方法,定义如下:

def die(self):
    if self.owner:
        self.owner.pets.discard(self)
    self.owner = None
    self.dead = True

现在我不想对死狗使用大多数其他方法,即人领养宠物的方法(person.adopt(self,pet))。由于这种情况有很多方法(可能还会有更多方法),我想避免在每个要求狗活着的方法中添加 if 语句。有没有办法在给定条件(例如 self.dead == False)的情况下根本不允许某些方法?

【问题讨论】:

  • 你的例子是对人的一种方法,而不是对宠物的一种方法。因此,您需要在该人的 adopt 方法中使用 if 语句来查看宠物是否已死亡;不是宠物的方法。
  • 对,但是我有很多方法需要亲自检查宠物是否还活着,有没有更优雅的方法?
  • 您希望不允许的方法做什么:引发 AttributeError(即,该方法只是从类中消失),引发 NotImplementedError,引发另一个错误(例如,ValueError),还是什么都不做(类似于pass)?
  • 好吧,我想我真正要问的是处理这种情况的更可取的方法,不难想象这种东西在很多其他地方使用,所以我只是想了解其他人如何处理它以及是否有一些首选方式。

标签: python class methods


【解决方案1】:

使用装饰器怎么样?您将不得不装饰每个需要宠物活着的函数,但您将避免一遍又一遍地编写 if 逻辑:

>>> def require_alive(func):
...     def wrapper(self, *args, **kwargs):
...         if not self.alive:
...             raise Exception("Not alive")
...         return func(self, *args, **kwargs)
...     return wrapper
...
>>> def require_hungry(func):
...     def wrapper(self, *args, **kwargs):
...         if not self.hungry:
...             print "Not hungry..."
...         else:
...             return func(self, *args, **kwargs)
...     return wrapper
...
>>> class Pet(object):
...     def __init__(self):
...         self.alive = True
...         self.hungry = True
...     def die(self):
...         self.alive = False
...     @require_alive
...     @require_hungry
...     def eat(self):
...         print "Eating..."
...         self.hungry = False
...     @require_alive
...     def sleep(self):
...         print "Sleeping..."
...
>>> roofus = Pet()
>>> roofus.eat()
Eating...
>>> roofus.eat()
Not hungry...
>>> roofus.sleep()
Sleeping...
>>> roofus.die()
>>> roofus.eat()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in wrapper
Exception: Not alive

【讨论】:

  • 可以为Person方法定义类似的装饰器,例如adopt,需要检查显式参数而不是self
  • 有趣,我以前从未遇到过装饰器,但它似乎正是我所寻找的。 @require_alive 装饰器是在每个方法之前还是在所有需要狗活着的方法之前都需要?
  • 您定义一次装饰器 (def require_alive...) 并在 每个 要求狗活着的方法之前包含装饰器 (@require_alive)。装饰器第一次看到时很难理解,但一旦理解就非常强大。
  • 抱歉,我的后续问题可能措辞不佳,我想问的是我是否需要在每个需要狗活着的函数之前或者只是在一大堆需要狗活着的函数之前使用装饰器还活着吗?
  • 我改写了我的评论以使其更清晰,并在 Pet 类中添加了一个额外的方法来演示。
【解决方案2】:

如果您有很多方法要装饰,您可能需要考虑使用元类来完成将其应用于所有方法的工作,即使它比您接受的答案更长。这样做的好处是装饰方法列表都在一个位置——因此更容易更改它们并提供文档。我还让装饰器本身成为该类的静态方法,因为这是唯一使用它的地方。

from functools import wraps

class MetaDeco(type):
    def __new__(metaclass, name, bases, attrs):
        cls = super(MetaDeco, metaclass).__new__(metaclass, name, bases, attrs)
        if 'meta_args' in attrs:
            decoratorname, methodnames = attrs['meta_args']
            decorator = getattr(cls, decoratorname)
            for methodname in methodnames:
                setattr(cls, methodname, decorator(getattr(cls, methodname)))
            del attrs['meta_args']  # clean up
        return cls

class Pet(object):
    __metaclass__ = MetaDeco  # in Python 3 use Pet(metalcass=MetaDeco)
    meta_args = ('require_alive', ('eat', 'sleep'))

    @staticmethod
    def require_alive(method):
        @wraps(method)
        def wrapper(self, *args, **kwargs):
            if self.dead:
                raise Exception("Not alive")
            return method(self, *args, **kwargs)
        return wrapper

    def __init__(self, owner):
        self.owner = owner
        self.dead = False

    def die(self):
        self.owner = None
        self.dead = True

    def eat(self):
        print("Eating...")

    def sleep(self):
        print("Sleeping...")

p = Pet('Alex')
p.eat()  # -> Eating...
p.sleep()  # -> Sleeping...
p.die()
p.eat()  # will raise exception

【讨论】:

    猜你喜欢
    • 2010-11-15
    • 1970-01-01
    • 2014-12-04
    • 1970-01-01
    • 2016-01-20
    • 2015-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多