Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/277.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
菊花链Python/Django自定义装饰器_Python_Django_Python Decorators - Fatal编程技术网

菊花链Python/Django自定义装饰器

菊花链Python/Django自定义装饰器,python,django,python-decorators,Python,Django,Python Decorators,菊花链Python/Django自定义装饰器是一种好的风格吗?并传递不同于收到的参数 我的许多Django视图函数都以完全相同的代码开始: @login_required def myView(request, myObjectID): try: myObj = MyObject.objects.get(pk=myObjectID) except: return myErrorPage(request) try:

菊花链Python/Django自定义装饰器是一种好的风格吗?并传递不同于收到的参数

我的许多Django视图函数都以完全相同的代码开始:

@login_required
def myView(request, myObjectID):
    try:
        myObj = MyObject.objects.get(pk=myObjectID)
    except:
        return myErrorPage(request)       

    try:
        requester = Profile.objects.get(user=request.user)
    except:
        return myErrorPage(request)

    # Do Something interesting with requester and myObj here
仅供参考,这是URL.py文件中相应条目的外观:

url(r'^object/(?P<myObjectID>\d+)/?$', views.myView, ),
下面是我的问题:

  • 这样做合法吗?这是好的款式吗?请注意,我将更改myView()函数的签名。这对我来说有点奇怪和危险。但我不知道为什么
  • 如果我制作了多个这样的装饰器来执行一些常见的函数,但是每个装饰器调用的包装函数的参数与收到的装饰器不同,那么我可以将它们菊花链在一起吗
  • 如果上面的#1和#2没有问题,那么向myView的用户指出他们应该传入的参数集的最佳方式是什么(因为仅仅查看函数定义中的参数不再真正有效)

  • 首先,这段代码:

    try:
        myObj = MyObject.objects.get(pk=myObjectID)
    except:
        return myErrorPage(request)
    
    可替换为:

    from django.shortcuts import get_object_or_404
    myObj = get_object_or_404(MyObject, pk=myObjectID)
    
    这同样适用于您拥有的第二块代码

    这本身就让它变得更加优雅


    如果您想进一步实现自己的decorator,最好的选择是将@login\u子类化为required。如果您正在传递不同的参数,或者不想这样做,那么您确实可以创建自己的装饰器,而且不会出错。

    这是一个非常有趣的问题。但它并没有提供很多关于修改参数的见解

    可堆叠装饰器 你可以在另一个问题上找到一个堆叠装饰的例子,下面的解释隐藏在一个非常、非常长和详细的答案中:

    是的,就这么简单@decorator只是一个快捷方式:

    another_stand_alone_function = my_shiny_new_decorator(another_stand_alone_function)
    
    这就是魔法。如前所述:装饰器是一个返回另一个函数的函数

    这意味着您可以:

    from functools import wraps
    
    def decorator1(f):
        @wraps(f)
        def wrapper(*args, **kwargs):
            do_something()
            f(*args, **kwargs)
        return wrapper
    
    
    def decorator2(f):
        @wraps(f)
        def wrapper(*args, **kwargs):
            do_something_else()
            f(*args, **kwargs)
        return wrapper
    
    @decorator1
    @decorator2
    def myfunc(n):
        print "."*n
    
    #is equivalent to 
    
    def myfunc(n):
        print "."*n
    myfunc = decorator1(decorator2(myfunc))
    
    装饰者不是装饰者 Python装饰程序可能会让开发人员感到困惑,他们使用一种语言学习OOP,而GoF已经使用了词典的一半来命名修复该语言故障的模式,这就是事实上的设计模式商店

    GoF的decorator是它们所修饰的组件(接口)的子类,因此与该组件的任何其他子类共享该接口

    Python装饰器是返回函数()的函数

    功能一直在下降 python装饰器是一个返回函数的函数,任何函数

    大多数装饰器的设计都是为了扩展装饰的功能,而不妨碍它的预期行为。它们是根据GoF对Decorator模式的定义而形成的,Decorator模式描述了一种在保持对象接口的同时扩展对象的方法

    但是GoF的Decorator是一种模式,而python的Decorator是一种特性

    Python装饰器是函数,这些函数应该返回函数(当提供函数时)

    适配器 让我们采用另一种GoF模式:

    适配器帮助两个不兼容的接口协同工作。 这是适配器的真实定义

    [对象]适配器包含其包装的类的实例。 在这种情况下,适配器将调用包装的 反对

    以一个对象为例,比如一个调度程序,它调用一个函数,该函数接受一些已定义的参数,并接受一个函数,该函数将执行该任务,但提供另一组参数。第二个函数的参数可以从第一个函数的参数导出

    一个函数(python中的一类对象)是一个适配器,它将获取第一个函数的参数并派生它们来调用第二个函数,然后返回从其结果派生的值

    为传递的函数返回适配器的函数将是适配器工厂

    Python装饰器是返回函数的函数。包括适配器

    def my_adapter(f):
        def wrapper(*args, **kwargs):
            newargs, newkwargs = adapt(args, kwargs)
            return f(*newargs, **newkwargs)
    
    @my_adapter # This is the contract provider
    def myfunc(*args, **kwargs):
        return something()
    
    哦,我知道你在那里做了什么…这是一种好的风格吗? 我会说,该死的,是的,又是一个内置模式!但您必须忘记GoF装饰器,只需记住python装饰器是返回函数的函数。因此,您正在处理的接口是包装函数的接口,而不是装饰的接口

    一旦你装饰了一个函数,装饰者就会定义契约,要么告诉它保留装饰函数的接口,要么把它抽象掉。你不再调用装饰函数了,尝试它甚至很困难,你调用包装器。

    1)是的,链接装饰器是有效的,正如其他答案已经指出的那样。好的风格是主观的,但我个人认为它会使你的代码更难为他人阅读。熟悉Django但不熟悉您的应用程序的人在使用您的代码时需要在头脑中保留额外的上下文。我认为坚持框架约定以使代码尽可能可维护是非常重要的

    2)答案是肯定的,将不同的参数传递给被包装的函数在技术上是可以的,但是考虑一个简单的代码示例:这是如何工作的:

    def decorator1(func):
        def wrapper1(a1):
            a2 = "hello from decorator 1"
            func(a1, a2)
        return wrapper1
    
    def decorator2(func):
        def wrapper2(a1, a2):
            a3 = "hello from decorator 2"
            func(a1, a2, a3)
        return wrapper2
    
    @decorator1
    @decorator2
    def my_func(a1, a2, a3):
        print a1, a2, a3
    
    my_func("who's there?")
    
    # Prints:
    # who's there?
    # hello from decorator 1
    # hello from decorator2
    
    在我看来,任何读到这篇文章的人都需要是一名心理体操运动员,才能在装饰器堆栈的每一层保留方法签名的上下文

    3) 我将使用基于类的视图并重写
    dispatch()
    方法来设置如下实例变量:

    @login_required
    @my_decorator
    def myView(request, requester, myObj):        
        # Do Something interesting with requester and myObj here
    
    class MyView(View):
        @method_decorator(login_required)
        def dispatch(self, *args, **kwargs):
            self.myObj = ...
            self.requester = ...
            return super(MyView, self).dispatch(*args, **kwargs)
    
    dispatch
    方法是调用
    get()/post()
    方法的方法。从django文档:

    as_视图入口点创建类的实例并调用其dispatch()方法。dispatch查看请求以确定它是否是GET、POST等,如果定义了匹配方法,则将请求转发给匹配方法

    然后可以在
    get()
    和/或
    post()
    视图方法中访问这些实例变量。这种方法的优点是
    class MyView(View):
        def get(self, request, id):
            print 'requester is {}'.format(self.requester)