j********x 发帖数: 2330 | 1 mutating:
def foo(dict_arg):
dict_arg.pop("xxx")
if dict_arg == yyy:
# do something
non-mutating:
def foo(dict_arg):
if DictEqualExcept(dict_arg, yyy, "xxx"):
# do something
哪种比较好,需要其他细节我可以补充 |
d****i 发帖数: 4809 | 2 你这个两个貌似都没问题吧,就好比C里面经常有
int foo(struct Person *pp, const struct Name *pn);
前一个mutable(一般既是输入也是输出),后一个immutable(只是输入)。
【在 j********x 的大作中提到】 : mutating: : def foo(dict_arg): : dict_arg.pop("xxx") : if dict_arg == yyy: : # do something : non-mutating: : def foo(dict_arg): : if DictEqualExcept(dict_arg, yyy, "xxx"): : # do something : 哪种比较好,需要其他细节我可以补充
|
j********x 发帖数: 2330 | 3 啥问题,mutate的应该有一个参数不mutate
不mutate的应该有个参数mutate? |
p**o 发帖数: 3409 | 4 没有特别“不鼓励”。如果要对传入对象的内部状态进行变更,当然只能传入mutable
object。就算不变更,一般也是照样传进去。
如果你不完全信任你要调用的函数、要确保它无法更改你传入对象的状态(一般这会被
认为是不可思议的需求),可以转成immutable object传入,比如list转tuple,set转
frozenset,一般对象做deepcopy,当然这样就无谓地增加了overhead。
【在 j********x 的大作中提到】 : mutating: : def foo(dict_arg): : dict_arg.pop("xxx") : if dict_arg == yyy: : # do something : non-mutating: : def foo(dict_arg): : if DictEqualExcept(dict_arg, yyy, "xxx"): : # do something : 哪种比较好,需要其他细节我可以补充
|
p**o 发帖数: 3409 | 5 对。有const关键字就可以解决这个问题。
python这样的动态语言简化了设计,但从函数签名看不出这个这个函数要不要对入参进
行变更,实际增加了心智负担。Python3引入function annotation可以缓解部分问题,
但不强制做类型检查,属于君子协定。
【在 d****i 的大作中提到】 : 你这个两个貌似都没问题吧,就好比C里面经常有 : int foo(struct Person *pp, const struct Name *pn); : 前一个mutable(一般既是输入也是输出),后一个immutable(只是输入)。
|
p**o 发帖数: 3409 | 6 没怎么看明白。要不你把具体场景展开说一说?
【在 j********x 的大作中提到】 : 啥问题,mutate的应该有一个参数不mutate : 不mutate的应该有个参数mutate?
|
j********x 发帖数: 2330 | 7 一个直接后果是,同样的参数,两次调用之间结果不同
而且根据这里的目的“忽略dict中某个key的值”进行比较;本身应该用直观的方法表
现,所谓pop之后再比较,理解起来并不容易。起码我本人看了3遍才明白
mutable
【在 p**o 的大作中提到】 : 没有特别“不鼓励”。如果要对传入对象的内部状态进行变更,当然只能传入mutable : object。就算不变更,一般也是照样传进去。 : 如果你不完全信任你要调用的函数、要确保它无法更改你传入对象的状态(一般这会被 : 认为是不可思议的需求),可以转成immutable object传入,比如list转tuple,set转 : frozenset,一般对象做deepcopy,当然这样就无谓地增加了overhead。
|
p**o 发帖数: 3409 | 8 “同样的参数,两次调用之间结果不同”,一般指的是默认参数的问题,
感兴趣的话可以参考这个讨论:
http://www.newsmth.net/bbstcon.php?board=Python&gid=84864
但在你这个具体场景,并不是“同样的参数”,因为两次传入的对象的状态不一样(从
dict里面pop一对key-value出来是对原dict的变更)。
你的这个具体需求:“忽略dict中某个key的值”,为了不更改原dict,可以在函数里
把原dict拷贝一份再pop再比较啊。就是我前面说的。
In [18]: d1 = {1:11, 2:22, 3:33, 4:44}
In [19]: d2 = {1:11, 2:22, 3:33}
In [20]: d1a = d1.copy()
In [21]: d1a.pop(4)
Out[21]: 44
In [22]: d1a == d2
Out[22]: True
In [23]: d1
Out[23]: {1: 11, 2: 22, 3: 33, 4: 44}
【在 j********x 的大作中提到】 : 一个直接后果是,同样的参数,两次调用之间结果不同 : 而且根据这里的目的“忽略dict中某个key的值”进行比较;本身应该用直观的方法表 : 现,所谓pop之后再比较,理解起来并不容易。起码我本人看了3遍才明白 : : mutable
|
j********x 发帖数: 2330 | |
p**o 发帖数: 3409 | 10
如果你嫌copy有开销,自己定制一个又不是难事:
def dict_equal_except (dict_a, dict_b, except_key):
assert except_key in dict_a
if len(dict_a) != len(dict_b) + 1:
return False
for key, aval in dict_a.iteritems():
if key != except_key and (key not in dict_b
or dict_b[key] != aval):
retur False
return True
但CPython循环一般比C慢2-3个数量级,dict不大时还不如直接copy。
参考 CPython 实现:
http://hg.python.org/cpython/file/2.7/Objects/dictobject.c
static int
dict_equal(PyDictObject *a, PyDictObject *b)
{
Py_ssize_t i;
if (a->ma_used != b->ma_used)
/* can't be equal if # of entries differ */
return 0;
/* Same # of entries -- check all of 'em. Exit early on any diff. */
for (i = 0; i <= a->ma_mask; i++) {
PyObject *aval = a->ma_table[i].me_value;
if (aval != NULL) {
int cmp;
PyObject *bval;
PyObject *key = a->ma_table[i].me_key;
/* temporarily bump aval's refcount to ensure it stays
alive until we're done with it */
Py_INCREF(aval);
/* ditto for key */
Py_INCREF(key);
bval = PyDict_GetItem((PyObject *)b, key);
Py_DECREF(key);
if (bval == NULL) {
Py_DECREF(aval);
return 0;
}
cmp = PyObject_RichCompareBool(aval, bval, Py_EQ);
Py_DECREF(aval);
if (cmp <= 0) /* error or not equal */
return cmp;
}
}
return 1;
}
【在 j********x 的大作中提到】 : copy一次这种脱了裤子放屁的就算了。。。
|
j********x 发帖数: 2330 | 11 倒不是开销问题 我一般试图简化函数约定
如果code base绝大部份函数都不修改参数 那一般情况下修改参数是不鼓励的
而且修改参数的比较方法对我来说不容易理解 开销在我这里不是大问题 输入都很小
100元素以内 |