在Python中可能的“类单子”

39 投票
8 回答
17719 浏览
提问于 2025-04-17 08:22

我在想办法整理一下我的代码。

在我的Python代码中,有这样的内容:

company = None
country = None

person = Person.find(id=12345)
if person is not None: # found        
    company = Company.find(person.companyId)

    if company is not None:
         country = Country.find(company.countryId)

return (person, company, country)

我看过关于Haskell中单子(特别是Maybe)的教程,想知道有没有其他写法。

8 个回答

20

Python 对于单子(monad)的语法并不是特别友好。不过,如果你想只使用类似于 Maybe 这样的单子(也就是说,你只能用 Maybe,不能写通用的函数来处理任何单子),你可以采用以下方法:

class Maybe():
    def andThen(self, action): # equivalent to Haskell's >>=
        if self.__class__ == _Maybe__Nothing:
            return Nothing
        elif self.__class__ == Just:
            return action(self.value)

    def followedBy(self, action): # equivalent to Haskell's >>
        return self.andThen(lambda _: action)

class _Maybe__Nothing(Maybe):
    def __repr__(self):
        return "Nothing"

Nothing = _Maybe__Nothing()

class Just(Maybe):
    def __init__(self, v):
        self.value = v
    def __repr__(self):
        return "Just(%r)" % self.value

接下来,把所有现在返回 None 的方法改成返回 Just(value) 或者 Nothing。这样你就可以写出以下代码:

Person.find(id=12345)
    .andThen(lambda person: Company.find(person.companyId))
    .andThen(lambda company: Country.find(company.countryId))

当然,你可以把这些 lambda 表达式改成用变量来存储中间结果;怎么做完全取决于你。

31

利用短路行为,以及自定义对象默认是“真”的,而None是“假”的特点:

person  = Person.find(id=12345)
company = person and person.company
country = company and company.country
42
company = country = None
try:
    person  =  Person.find(id=12345)
    company = Company.find(person.companyId)
    country = Country.find(company.countryId)
except AttributeError:
    pass # `person` or `company` might be None

EAFP 是一个编程术语,意思是“先尝试做,出错再说”。也就是说,在写代码的时候,我们先去尝试执行某个操作,如果遇到错误,再去处理这个错误。这种方法和“先检查再做”相对,后者是先确认一切都没问题再去执行。EAFP 的好处是代码通常会更简洁,因为我们不需要在执行之前做很多检查。

撰写回答