Как необязательный параметр может стать обязательным? - PullRequest
0 голосов
/ 04 марта 2019

На основании Отключить поиск глобальной переменной в Python (и мой собственный ответ там), у меня возникли проблемы при использовании функции с необязательными параметрами, такими как в этом минимальном примере:

import types

def noglobal(f):
    return types.FunctionType(f.__code__, {})

@noglobal
def myFunction(x=0):
    pass

myFunction()

По сути, это дает сбой следующим образом:

Traceback (most recent call last):
  File "SetTagValue.py", line 10, in <module>
    myFunction()
TypeError: myFunction() missing 1 required positional argument: 'x'

Почему x внезапно считается обязательным параметром?

Ответы [ 2 ]

0 голосов
/ 04 марта 2019

Это потому, что вы неправильно скопировали функцию.Если вы посмотрите на подпись types.FunctionType, то увидите, что она принимает 5 аргументов:

class function(object)
 |  function(code, globals, name=None, argdefs=None, closure=None)
 |
 |  Create a function object.
 |
 |  code
 |    a code object
 |  globals
 |    the globals dictionary
 |  name
 |    a string that overrides the name from the code object
 |  argdefs
 |    a tuple that specifies the default argument values
 |  closure
 |    a tuple that supplies the bindings for free variables

Вы не передали argdefs, поэтому у функции больше нет необязательных аргументов,Правильный способ копирования этой функции -

types.FunctionType(f.__code__,
                   {},
                   f.__name__,
                   f.__defaults__,
                   f.__closure__
                   )

Однако , это приводит к другой проблеме: ограничение доступа к глобальным переменным также отключает доступ к встроенным функциям.Если вы попытаетесь использовать print или open или dict или что-то подобное в myFunction, вы получите NameError.Итак, действительно правильный способ написать ваш декоратор таков:

import builtins
import types

def noglobal(f):
    return types.FunctionType(f.__code__,
                              {'__builtins__': builtins},
                              f.__name__,
                              f.__defaults__,
                              f.__closure__
                              )
0 голосов
/ 04 марта 2019

Если вы хотите сохранить значения аргументов по умолчанию, вам также необходимо передать их:

import types

def noglobal(f):
    return types.FunctionType(f.__code__, {}, f.__name__, f.__defaults__)

@noglobal
def myFunction(x=0):
    pass

myFunction()

Вы можете передать один последний closure параметр в types.FunctionType, который вы также можете унаследоватьс f.__closure__, если вы хотите, чтобы функции работали с замыканием.

...