Python 通过反射的环境变量
Python Environment Variables Through Reflection
在Python 3.7 中,我写了一个Environment
帮助程序class,它允许我通过反射加载OS 环境变量。它还根据派生 class.
中声明的类型提示,将环境变量转换为本机布尔值或元组。
它有效,但是 Visual Studio 代码中的 linter 给我这个问题报告:
Class 'Environment'
has no '__annotations__'
member
这里是帮手class。
import os
from abc import ABC
class Environment(ABC):
@classmethod
def from_os(cls):
convert = {
bool: lambda d: d == "1",
(): lambda d: tuple(d.split(',')),
str: lambda d: d
}
values = [
convert[value](os.environ[key])
for key,value in cls.__annotations__.items()
]
return cls(*values)
同样,代码确实有效。我该怎么做才能让 Pylint 开心?也许有一种不同的方法可以迭代属性(和属性类型提示),以便在不使用 __annotations__
?
的情况下获得相同的结果
我可以通过欺骗和添加 pylint 抑制提示让 linter 停止抱怨:
for key,value in cls.__annotations__.items() # pylint: disable=no-member
...但这不是我正在寻找的解决方案。
如果有帮助,下面是我的助手 class 如何用于获取任意一组环境变量的示例:
from dataclasses import dataclass
@dataclass
class FWMonitoringEnv(Environment):
# The names of these attributes are used
# to find and load a corresponding environment variable.
# This happens when "FWMonitoringEnv.from_os()" is called.
preempt : bool
split_routes : bool
tag_key : str
vpc_summary_route : str
route_table_id : str
fw_trust_enis : ()
fw_mgmt_ips : ()
api_key_name : str
region : str
在代码的其他地方我简单地执行了这个:
env = FWMonitoringEnv.from_os()
虽然我正在做这件事,但我还想解决另一个代码卫生问题。有没有办法让我的 Environment
class 强制派生的 class 成为 @dataclass
?例如,可以使用 Python 3.7 类型提示来完成吗?
只需使用 getattr
getattr(cls, "__annotations__").items()
以及整个源代码块
import os
from abc import ABC
class Environment(ABC):
@classmethod
def from_os(cls):
convert = {
bool: lambda d: d == "1",
(): lambda d: tuple(d.split(',')),
str: lambda d: d
}
values = [
convert[value](os.environ[key])
for key,value in getattr(cls, "__annotations__").items()
]
return cls(*values)
This will make the linter happy and your code working :) Also it is a good idea to add an assertion check that the cls
class has the required magic field __annotations__
.
您可以使用 https://docs.python.org/3/library/inspect.html#inspect.signature 方法获取所有 class 注释。
例子
pprint(inspect.signature(cls).parameters)
mappingproxy(OrderedDict([('preempt', <Parameter "preempt: bool">),
('split_routes', <Parameter "split_routes: bool">),
('tag_key', <Parameter "tag_key: str">),
('vpc_summary_route',
<Parameter "vpc_summary_route: str">),
('route_table_id', <Parameter "route_table_id: str">),
('fw_trust_enis', <Parameter "fw_trust_enis: ()">),
('fw_mgmt_ips', <Parameter "fw_mgmt_ips: ()">),
('api_key_name', <Parameter "api_key_name: str">),
('region', <Parameter "region: str">)]))
当合并到您的代码中时,它看起来如下:
def from_os(cls):
convert = {
bool: lambda d: d == '1',
(): lambda d: tuple(d.split(',')),
str: lambda d: d
}
values = [
convert[val.annotation](os.environ[val.name])
for val in signature(cls).parameters.values()
]
return cls(*values)
我相信这是获得它的更好方法 "true" :-)
在Python 3.7 中,我写了一个Environment
帮助程序class,它允许我通过反射加载OS 环境变量。它还根据派生 class.
它有效,但是 Visual Studio 代码中的 linter 给我这个问题报告:
Class
'Environment'
has no'__annotations__'
member
这里是帮手class。
import os
from abc import ABC
class Environment(ABC):
@classmethod
def from_os(cls):
convert = {
bool: lambda d: d == "1",
(): lambda d: tuple(d.split(',')),
str: lambda d: d
}
values = [
convert[value](os.environ[key])
for key,value in cls.__annotations__.items()
]
return cls(*values)
同样,代码确实有效。我该怎么做才能让 Pylint 开心?也许有一种不同的方法可以迭代属性(和属性类型提示),以便在不使用 __annotations__
?
我可以通过欺骗和添加 pylint 抑制提示让 linter 停止抱怨:
for key,value in cls.__annotations__.items() # pylint: disable=no-member
...但这不是我正在寻找的解决方案。
如果有帮助,下面是我的助手 class 如何用于获取任意一组环境变量的示例:
from dataclasses import dataclass
@dataclass
class FWMonitoringEnv(Environment):
# The names of these attributes are used
# to find and load a corresponding environment variable.
# This happens when "FWMonitoringEnv.from_os()" is called.
preempt : bool
split_routes : bool
tag_key : str
vpc_summary_route : str
route_table_id : str
fw_trust_enis : ()
fw_mgmt_ips : ()
api_key_name : str
region : str
在代码的其他地方我简单地执行了这个:
env = FWMonitoringEnv.from_os()
虽然我正在做这件事,但我还想解决另一个代码卫生问题。有没有办法让我的 Environment
class 强制派生的 class 成为 @dataclass
?例如,可以使用 Python 3.7 类型提示来完成吗?
只需使用 getattr
getattr(cls, "__annotations__").items()
以及整个源代码块
import os
from abc import ABC
class Environment(ABC):
@classmethod
def from_os(cls):
convert = {
bool: lambda d: d == "1",
(): lambda d: tuple(d.split(',')),
str: lambda d: d
}
values = [
convert[value](os.environ[key])
for key,value in getattr(cls, "__annotations__").items()
]
return cls(*values)
This will make the linter happy and your code working :) Also it is a good idea to add an assertion check that the
cls
class has the required magic field__annotations__
.
您可以使用 https://docs.python.org/3/library/inspect.html#inspect.signature 方法获取所有 class 注释。
例子
pprint(inspect.signature(cls).parameters)
mappingproxy(OrderedDict([('preempt', <Parameter "preempt: bool">),
('split_routes', <Parameter "split_routes: bool">),
('tag_key', <Parameter "tag_key: str">),
('vpc_summary_route',
<Parameter "vpc_summary_route: str">),
('route_table_id', <Parameter "route_table_id: str">),
('fw_trust_enis', <Parameter "fw_trust_enis: ()">),
('fw_mgmt_ips', <Parameter "fw_mgmt_ips: ()">),
('api_key_name', <Parameter "api_key_name: str">),
('region', <Parameter "region: str">)]))
当合并到您的代码中时,它看起来如下:
def from_os(cls):
convert = {
bool: lambda d: d == '1',
(): lambda d: tuple(d.split(',')),
str: lambda d: d
}
values = [
convert[val.annotation](os.environ[val.name])
for val in signature(cls).parameters.values()
]
return cls(*values)
我相信这是获得它的更好方法 "true" :-)