Есть ли способ напрямую ссылаться на пространство имен и его атрибуты изнутри в Python? - PullRequest
1 голос
/ 11 июля 2019

Я думаю об этом в течение некоторого времени с несколькими примерами, и я искренне думаю, что было бы удобно напрямую ссылаться на объект-метод изнутри кода метода или, вообще говоря: ссылаться на пространство имен изнутри.Давайте посмотрим на мой текущий код:

def do_stuff(args):
    logtarget, someargs = process_input(args)
    processed_data = do_other_stuff(someargs, outputdir)
    log("Template string".format(processed_data), outputdir=outputdir,  filename="resultfile.txt")

def do_other_stuff(someargs, logtarget):
    step = process_data(someargs)
    if requires_manual_postproceccing(step):
        log(step, outputdir=outputdir, filename="handle manually.txt")
    return get_result(step)

def log(message, outputdir="default", filename="default.txt"):
    with open("{}/{}".format(outputdir, filename), "a") as file:
        file.write(message)

Как вы можете видеть, я довольно часто передаю outputdir, и я даже не упомянул, что do_stuff и do_other_stuff могут также регистрировать события (но так какМне не нужны эти журналы, пока скрипт работает нормально, меня не волнует его местоположение).Разве не было бы хорошо, если бы кто-то мог установить параметр один раз и затем получить доступ к нему в сообщении журнала?Цель обрабатывается сценарием, поэтому я не могу жестко закодировать ее.

Поскольку outputdir обрабатывается в функции (пространстве имен), мы не можем получить к нему доступ из других функций (пространств имен), если не используем glogal ключевое слово, что - после всего, что я прочитал об этом - я надеялся никогда не использовать.Мои интуиции говорят мне, чтобы я справился с этой проблемой следующим образом:

def do_stuff(args):
    outputdir, someargs = process_input(args)
    log.outputdir = outputdir
    processed_data = do_other_stuff(someargs)
    log(processed_data.format("somehow"), filename="resultfile.txt")

def do_other_stuff(someargs):
    step = process_data(someargs)
    if requires_manual_posproceccing(step):
        log(step, filename="handle manually.txt")
    return get_result(step)

def log(message, filename="default.txt"):
    with open("{}/{}".format(this.outputdir, filename), "a") as file:
        file.write(message)

Его легче читать, он использует меньше аргументов и не вызывает странного поведения и ошибок - но это явно не работает.Теперь я знаю, что могу (должен?) Использовать необычную библиотеку журналов или, по крайней мере, написать класс logger, чтобы я мог использовать logger = Logger(outputdir), а затем использовать self внутри методов.Но сохранять его стройным и плоским - это способ программирования на Python, и до сих пор я мог легко следовать правилу: «Всякий раз, когда вы видите класс с одним методом, отличным от __init__, он должен быть просто функцией».Кроме того, даже когда я использовал этот класс регистратора или библиотеку журналирования, мне все равно приходилось передавать его экземпляр.

Это лучший способ справиться с этим, используя имя метода вместо thisи рискуют проблемы, когда кто-то украшает мои функции или есть способ напрямую ссылаться на текущее пространство имен и его атрибуты?Либо я не знаю подходящих слов для поиска, либо это слишком низкий уровень для документирования, либо он не существует.


[Edit:] На самом деле, что я действительно хотел сделать (ноне знал, что я хотел это), чтобы украсить (oop-cecorate, в отличие от Python-decorate) функцию log.Но все же я столкнулся бы с той же проблемой, поскольку log = decorate(log) будет влиять только на текущее пространство имен.dir возвращает текущее пространство имен, а не глобальное, поэтому, если вы не хотите использовать global, на самом деле нет другого пути, кроме как создать и передать вызываемый объект (callbackfunction).Прямо сейчас есть два ответа, и я не мог легко решить, какой из них яснее.Класс вызываемого , предложенный Мартином Боннером , работает со стандартным Python и создает только один вызываемый элемент вместо двух.С другой стороны, решение, которое я выбрал, - это get_log -функция, которая возвращает функцию, поскольку она еще более понятна, избегает создания класса только с одним методом, отличным от __init__, и требует меньше кода:

def get_log(outerdir):
    def log(message, filename="default.txt"):
        with open("{}/{}".format(outputdir, filename), "a") as file:
            file.write(message)
    return log

Если мы сделаем это более общим, создав круговой декоратор, который можно добавить к любой функции, мы просто воссоздадим partial функцию , предложенную heemayl , и мы приступим.Наиболее питонский подход не изобретен здесь и использует наименьший код.

Ответы [ 2 ]

1 голос
/ 11 июля 2019

Правильный способ связать функцию и некоторые данные - использовать класс. Вы можете даже заставить его вести себя так же, как ваше текущее решение, сделав функцию __call__:

class Logger(object):
    def __init__(self)
        self.outputdir="default"

    def __call__(self, message, filename="default.txt"):
        with open("{}/{}".format(self.outputdir, filename), "a") as file:
            file.write(message)

log = Logger()

def do_stuff(args):
    outputdir, someargs = process_input(args)
    log.outputdir = outputdir
    processed_data = do_other_stuff(someargs)
    log(processed_data.format("somehow"), filename="resultfile.txt")

...

1 голос
/ 11 июля 2019

Если я правильно понимаю, вы ищете способ сделать outputdir статическим для всех вызовов log. Если это так, вы можете использовать functools.partial:

from functools import partial
log_with_outputdir = partial(log, outputdir='/path/to/outputdir')

Теперь вы можете вызывать log_with_outputdir точно так же, как log без аргумента outputdir, поскольку он уже был применен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...