Pythonic способ использования типа в качестве аргумента функции - PullRequest
3 голосов
/ 12 октября 2019

Каким был бы самый питонический способ передачи типа объекта в качестве аргумента в функции?

Позвольте мне привести вам пример. Допустим, я пытался получить конфигурацию из переменной среды. Поскольку все переменные окружения являются строками, мне нужно привести значение к правильному типу.

Для этого мне нужно сообщить функции нужный тип. Это цель аргумента coerce. Мой первый инстинкт - передать желаемый тип как значение для coerce. Тем не менее, я не уверен, есть ли какие-либо последствия или проблемы в этом. тип. Однако я хотел бы знать, могут ли быть какие-либо проблемы, вызванные передачей встроенных типов в качестве аргументов функции.

Ответы [ 3 ]

2 голосов
/ 12 октября 2019

Поскольку все, что вы делаете с аргументом type, это сравниваете его один за другим с определенными типами, которые вы ожидаете, вместо того, чтобы фактически использовать type для создания объектов этого типа, он не делает ничего другогоот передачи строки, такой как 'str' или 'bool', в качестве аргумента и сравнения ее с несколькими строковыми константами.

Вместо этого вы можете сделать функции преобразования, такие как str2bool и str2int, сами по себеаргумент, так что вы можете вызвать coerce(value) для преобразования value в общем виде. Сохраните такие функции преобразования в качестве атрибутов выделенного класса для лучшей читаемости, как показано ниже:

import os
import typing

class to_type:
    bool = 'True'.__eq__
    int = int
    list = lambda s: s.split(',')

def get_config(config: str, coerce: typing.Callable = lambda s: s, default: any = None):
    value = os.getenv(config, None)
    if value is None:
        return default
    return coerce(value)

os.environ["TEST_VAR"] = "True"
print(get_config("TEST_VAR", to_type.bool))
os.environ["TEST_VAR"] = "2"
print(get_config("TEST_VAR", to_type.int))
os.environ["TEST_VAR"] = "a,b,c"
print(get_config("TEST_VAR", to_type.list))
os.environ["TEST_VAR"] = "foobar"
print(get_config("TEST_VAR"))

Это выводит:

True
2
['a', 'b', 'c']
foobar
2 голосов
/ 12 октября 2019

Вы можете упростить код и сделать его более мощным, просто напрямую передав функцию преобразования (аннотации типов оставлены в качестве упражнения):

def get_config(config, convert, default):
    value = os.getenv(config, None)
    return default if value is None else convert(value)

test_var = get_config("TEST_VAR", str2bool, False)

и, возможно, имея вспомогательную функцию для случая списка:

def make_str2list(delimiter=','):
    return lambda s: s.split(delimiter)

test_var = get_config("TEST_VAR", make_str2list(':'), [])
1 голос
/ 12 октября 2019

В этом конкретном случае я бы сказал, что coerce не должен быть типом , а скорее Enum : оба, потому что get_config поддерживает только небольшой набор возможных значенийи потому что он не использует значения типа непосредственно в своей обработке. Если ничего другого, то сигнатура функции более точна с помощью Enum.

ConfigType = Enum('ConfigType', 'STR BOOL INT LIST')

def get_config(config: str, coerce: ConfigType, default: any, delimiter: str = ","):
    value = os.getenv(config, None)  # Get config from environment
    if value is None:
        return default  # Return default if config is None

    if coerce is ConfigType.BOOL:
        value = str2bool(value)  # Cast config to bool
    elif coerce is ConfigType.INT:
        value = str2int(value)  # Cast config to int
    elif coerce is ConfigType.LIST:
        value = value.split(delimiter)  # Split string into list on delimiter

    return value  # Return the config value

Тем не менее, если вы действительно хотите использовать type , то Python 3.8 (который должен быть выпущен через два дня'time) поддерживает литеральные типы , что означает, что вы можете объявить функцию следующим образом:

def get_config(config: str, coerce: Literal[str, bool, int, list], default: any, delimiter: str = ","):
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...