英文:
When is a default object created in Python?
问题
我有一个类似以下结构的Python(3)代码:
- main_script.py
- util_script.py
- AccessClass.py
main
脚本调用 util
中的一个函数,其签名如下:
def migrate_entity(project, name, access=AccessClass.AccessClass()):
主脚本中的调用如下:
migrate_entity(project_from_file, name_from_args, access=access_object)
当调用完成时,所有对象都有值。然而,一旦执行 main
脚本,函数参数中的 AccessClass
默认就会被初始化,即使它从未被使用过。例如,这个 main
脚本的 __init__
会在函数签名中创建默认的类:
if __name__ == "__main__":
argparser = argparse.ArgumentParser(description='Migrate support data')
argparser.add_argument('--name', dest='p_name', type=str, help='The entity name to migrate')
load_dotenv()
fileConfig('logging.ini')
# Just for the sake of it
quit()
# The rest of the code...
# ...and then
migrate_entity(project_from_file, name_from_args, access=access_object)
即使添加了 quit()
,AccessClass
也会被创建。如果我使用 ./main_script.py -h
运行脚本,函数签名中的 AccessClass
也会被创建。尽管函数实际上只接收一个访问对象的调用,但我可以看到调用是发生在 AccessClass.__init__
中。
如果我用 None
替换默认值,然后在函数内部检查参数,然后再创建它,一切都按预期运行,也就是说,只有在需要时才会创建 AccessClass
。
请问有人能解释为什么会发生这种情况以及默认值的工作原理是什么吗?
在Python中,参数默认值是否总是提前创建的?
英文:
I have a Python (3) structure like following:
- main_script.py
- util_script.py
- AccessClass.py
The main
script is calling a function in util
with following signature:
def migrate_entity(project, name, access=AccessClass.AccessClass()):
The call itself in the main script is:
migrate_entity(project_from_file, name_from_args, access=access_object)
All objects do have values when the call is done.
However, As soon as the main
script is executed the AccessClass
in the function parameters defaults is initialized, even though it is never used. For example this main
script __init__
will create the default class in the function signature:
if __name__ == "__main__":
argparser = argparse.ArgumentParser(description='Migrate support data')
argparser.add_argument('--name', dest='p_name', type=str, help='The entity name to migrate')
load_dotenv()
fileConfig('logging.ini')
# Just for the sake of it
quit()
# The rest of the code...
# ...and then
migrate_entity(project_from_file, name_from_args, access=access_object)
Even with the quit()
added the AccessClass
is created. And if I run the script with ./main_script.py -h
the AccessClass
in the function signature is created. And even though the only call to the function really is with an access object I can see that the call is made to the AccessClass.__init__
.
If I replace the default with None
and instead check the parameter inside the function and then create it, everything is working as expected, i.e. the AccessClass
is not created if not needed.
Can someone please enlighten me why this is happening and how defaults are expected to work?
Are parameter defaults always created in advance in Python?
答案1
得分: 1
基本上,可变对象在你声明函数时就被初始化,而不是在调用函数时初始化。这就是为什么广泛不推荐将可变类型用作默认值的原因。你可以像你提到的那样使用 None
,然后在函数体内进行检查,如 if something is None
,然后进行适当的初始化。
def foo_bad(x = []): pass # 这是不好的
foo_bad() # 在声明时初始化的列表被使用
foo_bad([1,2]) # 使用提供的列表
foo_bad() # 再次使用在声明时初始化的列表
def foo_good(x = None):
if x is None:
x=[]
... # 进一步的逻辑
英文:
Basically the mutable objects are initialized the moment you declare the function, not when you invoke it. That's why it's widely discouraged to use mutable types as defaults. You can use None
as you mentioned and inside the body do the check if something is None
and then initialize it properly.
def foo_bad(x = []): pass # This is bad
foo_bad() # the list initialized during declaration used
foo_bad([1,2]) # provided list used
foo_bad() # again the list initialized during declaration used
def foo_good(x = None):
if x is None:
x=[]
... # further logic
答案2
得分: 1
AccessClass
是因为您将其设置为默认参数而被创建,因此它位于文件本身的范围内,并将在文件首次导入时初始化。这也是为什么不建议将列表或字典用作默认参数的原因。
如果未提供任何内容,这是定义默认值的一种更安全的方式:
def migrate_entity(project, name, access=None):
if access is None:
access = AccessClass.AccessClass()
您还可以使用类型提示来演示access
应该是什么类型:
def migrate_entity(project, name, access: Optional[AccessClass.AccessClass] = None):
英文:
AccessClass
is being created because you've set it as a default parameter, so it it's in the scope of the file itself and will be initialised when the file is first imported. This is also why it's not recommended to use lists or dicts as default parameters.
This is a much safer way of defining a default value if nothing is provided:
def migrate_entity(project, name, access=None):
if access is None:
access = AccessClass.AccessClass()
You could also use type hinting to demonstrate what type access
should be:
def migrate_entity(project, name, access: Optional[AccessClass.AccessClass] = None): ...
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论