1

我有这样的功能:

def foo(bar):
    ...

现在bar可以是 Django 记录,也可以是指向记录的 slug。我需要这种灵活性,以便当我手头有实际记录时可以调用这个函数,或者从一个更通用的函数调用这个函数,该函数只有 bar 作为字符串可用(从数据库中提取)。

我意识到我可以做类似的事情:

def foo(bar):
    try:
        bar.pk
    except AttributeError:
        bar = Bar.objects.get(slug=bar)
    ...

但这似乎一点也不优雅

如果可以的话,我想避免使用 isinstance。

4

3 回答 3

5

根据定义,您不使用 Duck Typing。Duck Typing 说:“如果它说话像鸭子,看起来像鸭子,那就是鸭子。”

Duck Typing 意味着您可以将两个完全不同类的对象传递给该方法并使其工作,因为它们都实现了相同的方法/属性(或优雅地处理丢失的方法/属性)。这意味着该方法从不关心它得到什么类型,只关心传递给它的任何对象都具有它期望使用的属性。

在您的情况下,您有时想传递一个对象和一个可用于在其他时间查找所述对象的字符串。这与 Duck Typing 无关。

isinstance 是解决这个问题的正确方法。在这种情况下,这是解决问题的最清晰方法,其他任何事情都会变得更复杂,更难理解,好处是 0。您可以在属性或 hasattr 上使用 try/except,但这可能会让任何未来的开发人员更加困惑。Duck Typing 很棒,它取代了强制转换各种子类以匹配某些特定功能,但在这种情况下,Duck Typing 并不适用。

简而言之。只需使用 isinstance。对于您的情况,这是正确的(pythonic)方法。

于 2012-04-03T23:05:20.840 回答
1

我不确定这是一种糟糕的处理方式,但如果我想做类似的事情,我可能会使用hasattr

def foo(bar):
    if hasattr(bar,"pk"):
        bar.pk
    else:
        # I include the str in case some other object with a __str__ happens
        # to come through.
        bar = Bar.objects.get(slug=str(bar))
于 2012-04-03T22:07:07.813 回答
0

这是另一种方法,可以帮助您执行相同的其他功能。我假设您使用的模型名称是“项目”。

def slug_resilient_decorator(class_type):

    def slug_resilient_wrapper(obj):

            if obj.has_attr('pk'):
                    return obj
            else:
                    return class_type.objects.get(slug=obj)

    return wrapper

@slug_resilient_decorator(Item)
def slug_resilient_detail_view(obj):

    ...
于 2012-04-04T01:45:48.813 回答