Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Understanding a function
#1
Hi,

I'd appreciate if someone can explain me the first function and what's used for since both yield the same results.

TIA.

@lambda _: _()
def func1() -> str:
	print('func1 was called')
	return 'some value here'


def func2():
	print('func2 was called')
	return 'some value there'


print(func1)
print(func2())
Reply
#2
The first function is not used for anything other than demonstrating using "@" and lambda to make a decorator. This decorator calls the decorated function when the decorated function name is encountered. You don't need () to call the function. In fact, calling func1() will raise a TypeError. Other than being obscure, I don't see a use for such a thing, but is a little bit like the way properties are implemented in a class. A property calls a method, but when you "call" the property it looks like you are getting or setting a variable. The property decorators call the method when the property name is encountered. No need for (), and most times using property() will result in a type error.
class Demo:
    def __init__(self, initial_value=0):
        self.property_value = initial_value

    @property
    def my_property(self):
        return self.property_value

    @my_property.setter
    def my_property(self, new_value):
        self.property_value = new_value


x = Demo()
print(x.my_property)
x.my_property = 5
print(x.my_property)
print(x.my_property())
Output:
0 5
Error:
File "...", line 18, in <module> print(x.my_property()) TypeError: 'int' object is not callable
ebolisa likes this post
Reply
#3
The code for func1 is equivalent to this
def func1() -> str:
    print('func1 was called')
    return 'some value here'
func1 = func1()
It could be useful to define a constant value without cluttering the global namespace with the code necessary to compute the value. For example
def VALUE():
    ... # some complicated computation
    return some_expression
VALUE = VALUE()
So it demonstrates how one can abuse Python's function syntax to build a single value. There are many such possibilities in Python. Recently, I shared a snippet demonstrating how on can abuse the class syntax to build a single dictionary.

Another similar idea I'm using sometimes is a small class fargs that represent function arguments. That is to say the fargs object stores the arguments and the keyword arguments of a function call and nothing else. It can then be used to call a function. Here is the class
from functools import partial

class fargs:
    """Object representing arguments of a function call"""
    __slots__ = ("args", "kwargs")

    def __init__(self, *args, **kwargs):
        self.args = args
        self.kwargs = kwargs

    def __call__(self, func):
        return func(*self.args, **self.kwargs)

    def partial(self, func):
        return partial(func, *self.args, **self.kwargs)

if __name__ == '__main__':

    def test(n, fruit='apple'):
        print(f'The monkey ate {n} {fruit}s')

    x = fargs(3, fruit='banana')
    x(test)

    @fargs()
    def func1():
        print('func1 was called')
        return('some value')
    print(func1)
So instead of @lambda _: _(), you could use @fargs() to achieve the same effect.
Output:
The monkey ate 3 bananas func1 was called some value
ebolisa likes this post
Reply
#4
It's a little cryptic(@lambda _: _()) way to write a no-op decorator.
As Gribouillis has written it or like this is more readable.
def no_op_decorator(func1):
    return func1

@no_op_decorator
def func1() -> str:
    print('func1 was called')
    return 'some value here'

print(func1() 
Output:
func1 was called some value here
The purpose of a no-op decorator is to wrap a function without altering its behavior.
It can serve as a placeholder decorator that can be easily removed or replaced when needed.
Can be used in eg Debugging,Testing,mocking or Conditional behavior: Depending on certain conditions or configurations.
To write a example that do someting,and is a no-op decorator.
import importlib

def requires_requests(func):
    try:
        importlib.import_module("requests")
        return func
    except ImportError:
        def noop(*args, **kwargs):
            raise ImportError("The 'requests' module is required but not installed")
        return noop

@requires_requests
def make_api_call():
    import requests
    response = requests.get('https://http.cat/200')
    print(response.status_code)

make_api_call()
Output:
200
Over i run it in a Python version that has Requests installed.
If i run it in a Python version that not has not Requests installed.
Error:
Traceback (most recent call last): File "<module1>", line 18, in <module> File "<module1>", line 9, in noop ImportError: The 'requests' module is required but not installed
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Having hard time understanding the function self-returning itself twice jagasrik 2 2,502 Aug-15-2020, 08:50 PM
Last Post: deanhystad
  Understanding Method/Function robdhunter 2 2,654 Mar-10-2018, 11:57 PM
Last Post: robdhunter
  Seeking understanding with the python import function. Intelligent_Agent0 2 2,622 Feb-18-2018, 11:57 PM
Last Post: snippsat

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020