# Macro for decorator (wrapper) in Python

**URL:** <https://discourse.julialang.org/t/macro-for-decorator-wrapper-in-python/46362>\
**Category:** New to Julia\
**Tags:** question\
**Created:** [September 10, 2020, 6:57am UTC](https://discourse.julialang.org/t/macro-for-decorator-wrapper-in-python/46362 "2020-09-10T06:57:04Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![iHany](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ihany/32/18151_2.png) [@iHany](https://discourse.julialang.org/u/iHany)\
**Post date:** [September 10, 2020, 6:57am UTC](https://discourse.julialang.org/t/macro-for-decorator-wrapper-in-python/46362/1 "2020-09-10T06:57:04Z")

</div>

Greetings,

What I’m trying to do can be expressed as a Python code like this:

```julia
def is_convex_repr(is_convex):
    def wrapper(func):
        func.is_convex = is_convex
        return func
    return wrapper

```

```julia
    @is_convex_repr(False)
    def my_obj_path(x, u):
        return 0.5*(x.T@x + u.T@u)

```

Then, `f = my_obj_path` will immediately give me that `f.is_convex = False`.

I found that macro in Julia can be used to do the same thing, but I’m not sure how to do it exactly with macro. Can anybody help me?

---

<div class="post-metadata">

**Author:** ![marius311](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/marius311/32/3953_2.png) [@marius311](https://discourse.julialang.org/u/marius311)\
**Post date:** [September 10, 2020, 8:30am UTC](https://discourse.julialang.org/t/macro-for-decorator-wrapper-in-python/46362/2 "2020-09-10T08:30:26Z")

</div>

The cleanest thing is probably to use a “trait”-like approach, you don’t even need a macro:

```julia
my_obj_path(x, y) = (x+y)/2

is_convex(::Any) = false
is_convex(::typeof(my_obj_path)) = true

is_convex(my_obj_path) # returns true
is_convex(some_other_function) # returns false

```

A cool thing is that since the trait is now known at compile-time, some extra optimizations may happen, eg branches that depend on `is_convex` will be optimized away entirely:

```julia
foo(func) = is_convex(func) ? 100 : 200

julia> @code_llvm foo(my_obj_path)

; @ REPL[4]:1 within `foo'
define i64 @julia_foo_2435() {
top:
  ret i64 100
}

```

You could then also write a macro that rewrites

```julia
@is_convex_repr true my_obj_path(x, y) = (x+y)/2

```

into something like the above code, if that’s the syntax you wanted.

---

<div class="post-metadata">

**Author:** ![xiaodai](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/xiaodai/32/15937_2.png) [@xiaodai](https://discourse.julialang.org/u/xiaodai)\
**Post date:** [September 10, 2020, 8:46am UTC](https://discourse.julialang.org/t/macro-for-decorator-wrapper-in-python/46362/3 "2020-09-10T08:46:16Z")

</div>

> [@marius311](#):
>
> `is_convex_repr(::typeof(my_obj_path)) = true`

this is dangerous right? I can define a function that operates on different types and is not convex.

---

<div class="post-metadata">

**Author:** ![marius311](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/marius311/32/3953_2.png) [@marius311](https://discourse.julialang.org/u/marius311)\
**Post date:** [September 10, 2020, 8:59am UTC](https://discourse.julialang.org/t/macro-for-decorator-wrapper-in-python/46362/4 "2020-09-10T08:59:39Z")

</div>

Yea, here convexity would be defined per-function rather than per-method (which is how I took the original question, since Python doesn’t have methods).

---

<div class="post-metadata">

**Author:** ![iHany](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ihany/32/18151_2.png) [@iHany](https://discourse.julialang.org/u/iHany)\
**Post date:** [September 10, 2020, 9:07am UTC](https://discourse.julialang.org/t/macro-for-decorator-wrapper-in-python/46362/5 "2020-09-10T09:07:30Z")

</div>

@marius311 Thanks for your kind explanation, but in my example code, `is_convex_repr` does not act to determine whether a function is convex or not. Instead, it is more close to add a method like

```julia
@is_convex_repr(False)
def my_obj_path(x, u):
    return 0.5*(x.T@x + u.T@u)

my_obj_path.is_convex == False # true

```

In my case, I have a bunch of functions and wanna initialise each function easily by decorating them (if I were using Python).

---

<div class="post-metadata">

**Author:** ![marius311](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/marius311/32/3953_2.png) [@marius311](https://discourse.julialang.org/u/marius311)\
**Post date:** [September 10, 2020, 9:11am UTC](https://discourse.julialang.org/t/macro-for-decorator-wrapper-in-python/46362/6 "2020-09-10T09:11:06Z")

</div>

Sorry should have called it just `is_convex` to more closely match your original question, I edited my post to reflect that. The only difference is that its a function `is_convex(my_obj_path)` instead of a property, `my_obj_path.is_convex`. If this isn’t what you’re asking for then I may be missing something.

---

<div class="post-metadata">

**Author:** ![xiaodai](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/xiaodai/32/15937_2.png) [@xiaodai](https://discourse.julialang.org/u/xiaodai)\
**Post date:** [September 10, 2020, 9:12am UTC](https://discourse.julialang.org/t/macro-for-decorator-wrapper-in-python/46362/7 "2020-09-10T09:12:47Z")

</div>

> [@marius311](#):
>
> since Python doesn’t have methods

exactly. hence it’s safe to do it in python but not so in Julia

---

<div class="post-metadata">

**Author:** ![xiaodai](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/xiaodai/32/15937_2.png) [@xiaodai](https://discourse.julialang.org/u/xiaodai)\
**Post date:** [September 10, 2020, 9:16am UTC](https://discourse.julialang.org/t/macro-for-decorator-wrapper-in-python/46362/8 "2020-09-10T09:16:36Z")

</div>

> [@iHany](#):
>
> `my_obj_path.is_convex == False # true`

you assign a value to something os you can detect whether it’s convex right? Not to store value in the function, that’s not your aim.

---

<div class="post-metadata">

**Author:** ![xiaodai](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/xiaodai/32/15937_2.png) [@xiaodai](https://discourse.julialang.org/u/xiaodai)\
**Post date:** [September 10, 2020, 9:22am UTC](https://discourse.julialang.org/t/macro-for-decorator-wrapper-in-python/46362/9 "2020-09-10T09:22:49Z")

</div>

This would be kinda weird to do in Julia but is kinda what u are looking for

```julia

struct IsConvexRepr{T}
    fn
    arg_types::T
    is_convex::Bool
end

function (icr::IsConvexRepr)(args...)
    @assert all(typeof.(args) .== icr.arg_types)
    icr.fn(args...)
end

using MacroTools
macro is_convex_repr(b, ex)
    @capture(ex, fn_(args__))
    :(
        IsConvexRepr($fn, typeof.($args), $b)
    )
end

abc(x,y) = x+y

# you can define if it's convex by supplying an example of what to 
# after all a method in julia is completely determined by function name and argument types
abc1 = @is_convex_repr false abc(1, 1)
abc2 = @is_convex_repr true abc(1.0, 1.0)

abc1(1, 1)
abc1(1.0, 1.0) # will error cos types don't match

abc2(1.0, 1.0)
abc2(1, 1) # will error cos types don't match

is_convex(x) = x.is_convex

is_convex(abc1) # false
abc1.is_convex # false

is_convex(abc2) # true
abc2.is_convex # true

```

---

<div class="post-metadata">

**Author:** ![lungben](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lungben/32/12314_2.png) [@lungben](https://discourse.julialang.org/u/lungben)\
**Post date:** [September 10, 2020, 1:30pm UTC](https://discourse.julialang.org/t/macro-for-decorator-wrapper-in-python/46362/10 "2020-09-10T13:30:39Z")

</div>

A decorator in Python is just syntactic sugar for a higher-order function.  
Example:

```julia
def dec(f):
       def inner(*args, **kwargs):
           print("hello!")
           return f(*args, **kwargs)
       return inner

@dec
def f(x):
    return 3*x

# is equivalent to 
def f(x):
    return 3*x
f = dec(f)

```

Julia has also higher-order functions, thus the same behavior can be archieved, too.

What is not possible in Julia is to give functions “object properties” from outside, like you did in Python:

```julia
func.is_convex = is_convex

```

However, I think this is quite an unusual way to use the Python object system. An alternative approach in Python would be to use a class here:

```julia
class FunctionWithProperty:
    def __init__ (self, f, is_callable):
        self.f = f
        self.is_callable = is_callable
    def __call__ (self, x):
        return self.f(x)

```

In Julia, the same could be archived with a Struct and a functor - see [Methods · The Julia Language](https://docs.julialang.org/en/v1/manual/methods/#Function-like-objects).
