Parametr funkcji: func.varName

Parametr funkcji: func.varName
Radosław Głębicki
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Sardine, Italy
  • Postów: 193
0

Witam

Kopiuj
#! /usr/bin/env python3
# -*- coding: utf-8 -*-

def aaa():
	print(aaa.myParam)
	aaa.myParam = 2
	return None

aaa.myParam = 3

print(aaa.myParam)

aaa()

print(aaa.myParam)

print(aaa.__dir__())

quit()

Print __dir__ i tam jest jako jeden z parametrów. I jest też jako jedyny w __dict__ tej funkcji. Do czego to można wykorzystać?

Pozdrawiam
Radek Glebicki

LukeJL
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 8533
1
Radosław Głębicki napisał(a):

Print __dir__ i tam jest jako jeden z parametrów. I jest też jako jedyny w __dict__ tej funkcji. Do czego to można wykorzystać?

Pozdrawiam
Radek Glebicki

__dir__ to magiczna metoda, z której korzysta funkcja dir:
https://docs.python.org/3/library/functions.html#dir

Spearhead
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 1015
2

W Pythonie funkcje są obiektami, dzięki czemu można je wstawiać do zmiennych, przekazywać jako parametry i tak dalej. Można również im przypisywać atrybuty.

Kopiuj
>>> def foo():
...     print("Hello, world")
... 
>>> foo.somevariable = 10
>>> print(foo.somevariable)
10

Na przykład funkcje mają standardowy atrybut __name__ zwracający nazwę funkcji:

Kopiuj
>>> def call_some_function(callback):
...     print(f"Calling function {callback.__name__}: {callback()}")
... 
>>> def foo():
...     return "ok"
... 
>>> call_some_function(foo)
Calling function foo: ok

Przypisanie jakiegoś niestandardowego atrybutu do funkcji można schować za dekoratorem dla ładniejszej składni:

Kopiuj
>>> def author(r):
...     def wrapper(f):
...         f.author = r
...         return f
...     return wrapper
... 
>>> @author("Spearhead")
... def foo():
...     print("Hello world")
... 
>>> foo()
Hello world
>>> foo.author
'Spearhead'

Można w ten sposób trzymać w funkcji jakieś tagi, liczniki czy flagi, w zależności od potrzeby.

Spearhead
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 1015
0

Jeszcze małe dopytanko. Z wnętrza funkcji __name__ nie da nam nazwy tej funkcji tylko moduł inspect daje takie możliwości?

Rozwińmy zatem trochę temat. Dobieranie się do __name__ z wnętrza funkcji działa podobnie jak i dla innych atrybutów:

Kopiuj
>>> def foo():
...     print(foo.__name__)
... 
>>> foo()
foo

Tylko, że to się mija z celem, skoro w środku funkcji foo musimy odnieść się do obiektu o nazwie foo, to równie dobrze można by napisać:

Kopiuj
def foo():
    print("foo")

Pytanie się zatem nasuwa, czy można by się ze środka funkcji dobrać do niej samej w sposób uniwersalny. Analogiczny problem pojawia się jeżeli używamy funkcji rekurencyjnych. Załóżmy, na przykład że mamy taką funkcję:

Kopiuj
def fibonacci(n):
   if n <= 1:
       return n
   else:
       return(fibonacci(n-1) + fibonacci(n-2))

Jeżeli chcielibyśmy zmienić nazwę takiej funkcji z fibonacci na fib to musimy to zrobić w trzech miejscach zamiast w jednym. Pytanie zatem, czy możemy tego uniknąć. Odpowiedź brzmi (o ile mi wiadomo) nie. Dokument PEP 3130 zaproponował wprowadzenie słowa kluczowego __function__, który w sposób uniwersalny odnosiłby się do bieżącej funkcji wewnątrz bloku kodu (https://peps.python.org/pep-3130/) ale został odrzucony. Można by próbować obchodzić to ograniczenie analizując ramki stosu (na tym polegają rozwiązania podane w https://stackoverflow.com/questions/5067604/determine-function-name-from-within-that-function), ale to są hacki i wolałbym na nich nie polegać, szczególnie gdy mamy funkcję rekurencyjną i nasz stos jest wielopiętrowy.

Dodajmy jeszcze, że __name__ można modyfikować. Robi to na przykład functools.wraps. Załóżmy, że mamy dekorator twice, który wywołuje dekorowaną funkcję dwa razy. Jeżeli użyjemy go na jakiejś funkcji to stracimy oryginalną nazwę i dokumentację:

Kopiuj
>>> def twice(f):
...     def wrapper(*args, **kwrags):
...         f(*args, **kwrags)
...         f(*args, **kwrags)
...     return wrapper
... 
>>> @twice
... def foo():
...     """Function documentation"""
...     print("Hello world")
... 
>>> foo()
Hello world
Hello world
>>> print(foo.__name__)
wrapper
>>> print(foo.__doc__)
None

Rozwiązaniem jest zastosowanie functools.wraps, które modyfikuje atrybuty __name__ i __doc__ zwracanego wrappera tak, aby wskazywały na oryginał:

Kopiuj
>>> import functools
>>> 
>>> def twice(f):
...     @functools.wraps(f)
...     def wrapper(*args, **kwrags):
...         f(*args, **kwrags)
...         f(*args, **kwrags)
...     return wrapper
... 
>>> @twice
... def foo():
...     """Function documentation"""
...     print("Hello world")
... 
>>> print(foo.__name__)
foo
>>> print(foo.__doc__)
Function documentation

O ile sam __name__ jest jak widać modyfikowalny, to istnieje również atrybut tylko do odczytu __code__.co_name:

Kopiuj
>>> foo.__code__.co_name
'foo'

Moduł inspect operuje bodajże na tych wartościach, a __name__ jest dodawane dla wygody i stosowania na co dzień.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.