gpt4 book ai didi

python - 如何检索传递给函数调用的关键字参数的原始顺序?

转载 作者:行者123 更新时间:2023-12-01 23:44:48 26 4
gpt4 key购买 nike

检索通过 **kwargs 传递的关键字参数的顺序在我正在处理的特定项目中非常有用。它是关于制作一种具有有意义维度的 n-d numpy 数组(现在称为dimarray),对于地球物理数据处理特别有用。

现在说我们有:

import numpy as np
from dimarray import Dimarray # the handy class I am programming

def make_data(nlat, nlon):
""" generate some example data
"""
values = np.random.randn(nlat, nlon)
lon = np.linspace(-180,180,nlon)
lat = np.linspace(-90,90,nlat)
return lon, lat, values

什么工作:
>>> lon, lat, values = make_data(180,360)
>>> a = Dimarray(values, lat=lat, lon=lon)
>>> print a.lon[0], a.lat[0]
-180.0 -90.0

什么不:
>>> lon, lat, data = make_data(180,180) # square, no shape checking possible !
>>> a = Dimarray(values, lat=lat, lon=lon)
>>> print a.lon[0], a.lat[0] # is random
-90.0, -180.0 # could be (actually I raise an error in such ambiguous cases)

原因是Dimarray的 __init__方法的签名是 (values, **kwargs)kwargs是一个无序的字典(dict),它可以做的最好的事情就是检查 values 的形状.

当然,我希望它适用于任何类型的维度:
a = Dimarray(values, x1=.., x2=...,x3=...)

所以它必须用 **kwargs 硬编码
发生歧义的几率随着维度的增加而增加。
有很多方法可以解决这个问题,例如使用签名 (values, axes, names, **kwargs)可以这样做:
a = Dimarray(values, [lat, lon], ["lat","lon"]) 

但是这种语法对于交互式使用(ipython)来说很麻烦,因为我希望这个包真正成为我(和其他人!!)日常使用python的一部分,作为地球物理学中numpy数组的实际替代品。

我会对解决这个问题的方法非常感兴趣。我现在能想到的最好的方法是使用检查模块的堆栈方法来解析调用者的语句:
import inspect
def f(**kwargs):
print inspect.stack()[1][4]
return tuple([kwargs[k] for k in kwargs])

>>> print f(lon=360, lat=180)
[u'print f(lon=360, lat=180)\n']
(180, 360)

>>> print f(lat=180, lon=360)
[u'print f(lat=180, lon=360)\n']
(180, 360)

人们可以从中解决一些问题,但是存在无法解决的问题,因为 stack() 捕获了所有内容:
>>> print (f(lon=360, lat=180), f(lat=180, lon=360))
[u'print (f(lon=360, lat=180), f(lat=180, lon=360))\n']
[u'print (f(lon=360, lat=180), f(lat=180, lon=360))\n']
((180, 360), (180, 360))

有没有我不知道的其他检查技巧可以解决这个问题? (我对这个模块不熟悉)我想得到括号中的一段代码 lon=360, lat=180应该是可行的,不是吗??

所以我第一次在python中有一种感觉,根据所有可用信息(用户提供的排序是有值(value)的信息!!!),在做一些理论上可行的事情方面遇到了困难。

我在那里读到尼克的有趣建议: https://mail.python.org/pipermail/python-ideas/2011-January/009054.html
并且想知道这个想法是否以某种方式取得了进展?

我明白为什么通常不希望有一个有序的 **kwargs,但是对于这些罕见情况的补丁会很整洁。有人知道可靠的黑客吗?

注意:这与 Pandas 无关,我实际上是在尝试为它开发一个轻量级的替代品,它的用法仍然非常接近 numpy。很快会发布 gitHub 链接。

编辑:注意我这与 相关互动 使用dimarray。无论如何都需要双重语法。

EDIT2:我还看到反论点,即知道数据是 不是 有序也可以被视为有值(value)的信息,因为它让 Dimarray 自由检查 values形状和自动调整顺序。甚至可能不记得数据的维度比两个维度具有相同的大小更常见。所以现在,我想对于不明确的情况提出错误是可以的,要求用户提供 names争论。尽管如此,拥有做出这种选择的自由(Dimarray 类应该如何表现)会很不错,而不是受到 python 缺少的特性的限制。

编辑 3 , SOLUTIONS: 在 kazagistar 的建议下:

我没有提到还有其他可选的属性参数,例如 name=""units="" ,以及其他一些与切片相关的参数,所以 *args构造需要在 kwargs 上带有关键字名称测试.

总结一下,有很多种可能:

*选择a:保持当前语法
a = Dimarray(values, lon=mylon, lat=mylat, name="myarray")
a = Dimarray(values, [mylat, mylon], ["lat", "lon"], name="myarray")

*选择 b:kazagistar 的第二个建议,通过 **kwargs 删除轴定义
a = Dimarray(values, ("lat", mylat), ("lon",mylon), name="myarray")

*选择 c:kazagistar 的第二个建议,通过 **kwargs 提供可选的轴定义
(注意这涉及从 names= 中提取的 **kwargs ,参见下面的背景)
a = Dimarray(values, lon=mylon, lat=mylat, name="myarray")
a = Dimarray(values, ("lat", mylat), ("lon",mylon), name="myarray")

*选择 d:kazagistar 的第三个建议,通过 **kwargs 提供可选的轴定义
a = Dimarray(values, lon=mylon, lat=mylat, name="myarray")
a = Dimarray(values, [("lat", mylat), ("lon",mylon)], name="myarray")

嗯,这归结为美学和一些设计问题(在交互模式下,懒惰订购是一个重要功能吗?)。我在 b) 和 c) 之间犹豫。我不确定 **kwargs 真的带来了什么。具有讽刺意味的是,我开始批评的东西在仔细考虑后变成了一个功能......

非常感谢您的回答。我会将问题标记为已回答,但非常欢迎您为 a)、b) c) 或 d) 投票!

======================

编辑 4 : 更好的解决方案 : 选择 a) !!,但添加一个 from_tuples 类方法。这样做的原因是允许多一个自由度。如果未提供轴名称,它们将自动生成为“x0”、“x1”等......使用起来就像 Pandas 一样,但使用轴命名。这也避免了将轴和属性混合到 **kwargs 中,并将其仅用于轴。一旦我完成了文档,就会有更多。
a = Dimarray(values, lon=mylon, lat=mylat, name="myarray")
a = Dimarray(values, [mylat, mylon], ["lat", "lon"], name="myarray")
a = Dimarray.from_tuples(values, ("lat", mylat), ("lon",mylon), name="myarray")

编辑 5 : 更多pythonic解决方案? :在用户 api 方面类似于上面的 EDIT 4,但通过包装器 dimarray,同时对 Dimarray 的实例化方式非常严格。这也符合 kazagistar 提出的精神。
 from dimarray import dimarray, Dimarray 

a = dimarray(values, lon=mylon, lat=mylat, name="myarray") # error if lon and lat have same size
b = dimarray(values, [("lat", mylat), ("lon",mylon)], name="myarray")
c = dimarray(values, [mylat, mylon, ...], ['lat','lon',...], name="myarray")
d = dimarray(values, [mylat, mylon, ...], name="myarray2")

从类(class)本身来看:
 e = Dimarray.from_dict(values, lon=mylon, lat=mylat) # error if lon and lat have same size
e.set(name="myarray", inplace=True)
f = Dimarray.from_tuples(values, ("lat", mylat), ("lon",mylon), name="myarray")
g = Dimarray.from_list(values, [mylat, mylon, ...], ['lat','lon',...], name="myarray")
h = Dimarray.from_list(values, [mylat, mylon, ...], name="myarray")

在 d) 和 h) 的情况下,轴会自动命名为“x0”、“x1”等,除非 mylat、mylon 实际上属于 Axis 类(我在这篇文章中没有提到,但是 Axes 和 Axis 会执行它们的操作)工作,构建轴并处理索引)。

说明:
class Dimarray(object):
""" ndarray with meaningful dimensions and clean interface
"""
def __init__(self, values, axes, **kwargs):
assert isinstance(axes, Axes), "axes must be an instance of Axes"
self.values = values
self.axes = axes
self.__dict__.update(kwargs)

@classmethod
def from_tuples(cls, values, *args, **kwargs):
axes = Axes.from_tuples(*args)
return cls(values, axes)

@classmethod
def from_list(cls, values, axes, names=None, **kwargs):
if names is None:
names = ["x{}".format(i) for i in range(len(axes))]
return cls.from_tuples(values, *zip(axes, names), **kwargs)

@classmethod
def from_dict(cls, values, names=None,**kwargs):
axes = Axes.from_dict(shape=values.shape, names=names, **kwargs)
# with necessary assert statements in the above
return cls(values, axes)

这是技巧(示意性地):
def dimarray(values, axes=None, names=None, name=..,units=..., **kwargs):
""" my wrapper with all fancy options
"""
if len(kwargs) > 0:
new = Dimarray.from_dict(values, axes, **kwargs)

elif axes[0] is tuple:
new = Dimarray.from_tuples(values, *axes, **kwargs)

else:
new = Dimarray.from_list(values, axes, names=names, **kwargs)

# reserved attributes
new.set(name=name, units=units, ..., inplace=True)

return new

我们唯一松动的确实是 *args 语法,它无法容纳这么多
选项。不过没关系。

它也使子分类变得容易。对于这里的 Python 专家来说,这听起来如何?

(整个讨论实际上可以分为两部分)

======================

一些背景知识(编辑:部分过时,仅适用于情况 a)、b)、c)、d),以防万一您有兴趣:

*选择一个涉及:
def __init__(self, values, axes=None, names=None, units="",name="",..., **kwargs):
""" schematic representation of Dimarray's init method
"""
# automatic ordering according to values' shape (unless names is also provided)
# the user is allowed to forget about the exact shape of the array
if len(kwargs) > 0:
axes = Axes.from_dict(shape=values.shape, names=names, **kwargs)

# otherwise initialize from list
# exact ordering + more freedom in axis naming
else:
axes = Axes.from_list(axes, names)

... # check consistency

self.values = values
self.axes = axes
self.name = name
self.units = units

*选择 b) 和 c) 强加:
def __init__(self, values, *args, **kwargs):
...

b) 所有属性都通过 kwargs 自然传递,使用 self.__dict__.update(kwargs) .这是干净的。

c) 需要过滤关键字参数:
def __init__(self, values, *args, **kwargs):
""" most flexible for interactive use
"""
# filter out known attributes
default_attrs = {'name':'', 'units':'', ...}
for k in kwargs:
if k in 'name', 'units', ...:
setattr(self, k) = kwargs.pop(k)
else:
setattr(self, k) = default_attrs[k]

# same as before
if len(kwargs) > 0:
axes = Axes.from_dict(shape=values.shape, names=names, **kwargs)

# same, just unzip
else:
names, numpy_axes = zip(*args)
axes = Axes.from_list(numpy_axes, names)

这实际上非常好用,唯一的(次要)缺点是 name=""、units=""的默认参数和其他一些更相关的参数无法通过检查或完成访问。

*选择d:清除 __init__
def __init__(self, values, axes, name="", units="", ..., **kwaxes)

但确实有点冗长。

==========

编辑,仅供引用 :我最终使用了 axes 的元组列表参数,或者参数 dims=labels=分别用于轴名称和轴值。相关项目dimarray在github上。再次感谢 kazagistar。

最佳答案

不,您无法知道将项目添加到字典中的顺序,因为这样做会显着增加实现字典的复杂性。 (当你真的需要这个时,collections.OrderedDict 已经满足你了)。

但是,您是否考虑过一些基本的替代语法?例如:

a = Dimarray(values, 'lat', lat, 'lon', lon)

或(可能是最好的选择)
a = Dimarray(values, ('lat', lat), ('lon', lon))

或(最明确的)
a = Dimarray(values, [('lat', lat), ('lon', lon)])

但在某种程度上,这种需求排序本质上是位置性的。 **kwargs 经常被滥用作为标签,但参数名称通常不应该是“数据”,因为以编程方式设置很麻烦。只需将与元组关联的两部分数据明确,并使用列表来保留排序,并提供强断言+错误消息以明确输入何时无效以及原因。

关于python - 如何检索传递给函数调用的关键字参数的原始顺序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20314526/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com