我注意到Python让我这样做:
>>> {1: "foo"} < {2: "bar"}
True
Run Code Online (Sandbox Code Playgroud)
它允许我为列表,deques等做同样的事情.什么是<在Python中应用于字典时的语义?
一般来说,在哪里可以找到<任何给定类型集合的语义?在大多数情况下,似乎没有在文档中找到.例如:
>>> help(dict.__cmp__)
Help on wrapper_descriptor:
__cmp__(...)
x.__cmp__(y) <==> cmp(x,y)
>>> help(cmp)
Help on built-in function cmp in module __builtin__:
cmp(...)
cmp(x, y) -> integer
Return negative if x<y, zero if x==y, positive if x>y.
Run Code Online (Sandbox Code Playgroud)
我问,因为我有一个表格的元组列表(int, dict).我想根据第一个元素对这个数组进行排序,但如果第一个元素对于两个项目是相等的,那么我不关心第二个元素.我想知道是否myArray.sort()会做一些复杂的事情,包括在这种情况下通过dicts递归,或者它只返回一个任意值.
反转元组并反转列表会返回不同类型的对象:
>>> reversed((1,2))
<reversed at 0x7fffe802f748>
>>> reversed([1,2])
<list_reverseiterator at 0x7fffebdd4400>
Run Code Online (Sandbox Code Playgroud)
他们也一样dir.这两种类型都不是另一种类型的子类.
这是为什么?一个人可以做什么,另一个不能?
在python中,可以以这种方式链接运算符:
a op b op c
Run Code Online (Sandbox Code Playgroud)
评估为
a op b and b op c
Run Code Online (Sandbox Code Playgroud)
唯一的区别b是只评估一次(所以更像是t = eval(b); a op t and t op c).
从具有显式连接(使用)的等效版本可读且更简洁的观点来看,这是有利的and.
但是......我注意到链接表达式和等效表达式之间存在微小的性能差异,无论是3个操作数还是20个.当你计算这些操作时,这一点就变得很明显了.
import timeit
timeit.timeit("a <= b <= c", setup="a,b,c=1,2,3")
0.1086414959972899
timeit.timeit("a <= b and b <= c", setup="a,b,c=1,2,3")
0.09434155100097996
Run Code Online (Sandbox Code Playgroud)
和,
timeit.timeit("a <= b <= c <= d <= e <= f", setup="a,b,c,d,e,f=1,2,3,4,5,6")
0.2151330839988077
timeit.timeit("a <= b and b <= c and c <= d and …Run Code Online (Sandbox Code Playgroud) 与函数相反,类的主体在定义时执行:
class A(object):
print 'hello'
Run Code Online (Sandbox Code Playgroud)
日期:
hello
Run Code Online (Sandbox Code Playgroud)
为什么会这样?它与@classmethod/ @staticmethodmethods和class属性有关吗?
在昨天的一个问题中,在评论中,我开始知道在python __code__函数中的atrribute是可变的.因此我可以编写如下代码
def foo():
print "Hello"
def foo2():
print "Hello 2"
foo()
foo.__code__ = foo2.__code__
foo()
Run Code Online (Sandbox Code Playgroud)
产量
Hello
Hello 2
Run Code Online (Sandbox Code Playgroud)
我试过谷歌搜索,但要么因为没有信息(我非常怀疑这一点),或者关键字(__code__)不容易搜索,我找不到一个用例.
它似乎不是"因为Python中的大多数东西都是可变的"也是一个合理的答案,因为函数的其他属性 - __closure__和__globals__- 是显式只读的(来自Objects/funcobject.c):
static PyMemberDef func_memberlist[] = {
{"__closure__", T_OBJECT, OFF(func_closure),
RESTRICTED|READONLY},
{"__doc__", T_OBJECT, OFF(func_doc), PY_WRITE_RESTRICTED},
{"__globals__", T_OBJECT, OFF(func_globals),
RESTRICTED|READONLY},
{"__module__", T_OBJECT, OFF(func_module), PY_WRITE_RESTRICTED},
{NULL} /* Sentinel */
};
Run Code Online (Sandbox Code Playgroud)
为什么__code__在其他属性为只读时可写?
在Python中,与包含该类相同属性的字典相比,为类实例创建的字典很小:
import sys
class Foo(object):
def __init__(self, a, b):
self.a = a
self.b = b
f = Foo(20, 30)
Run Code Online (Sandbox Code Playgroud)
使用Python 3.5.2时,以下调用getsizeof生成:
>>> sys.getsizeof(vars(f)) # vars gets obj.__dict__
96
>>> sys.getsizeof(dict(vars(f))
288
Run Code Online (Sandbox Code Playgroud)
288 - 96 = 192 字节已保存!
但是,另一方面,使用Python 2.7.12,相同的调用返回:
>>> sys.getsizeof(vars(f))
280
>>> sys.getsizeof(dict(vars(f)))
280
Run Code Online (Sandbox Code Playgroud)
0 保存的字节数
在这两种情况下,词典显然具有完全相同的内容:
>>> vars(f) == dict(vars(f))
True
Run Code Online (Sandbox Code Playgroud)
所以这不是一个因素.此外,这也仅适用于Python 3.
那么,这里发生了什么?为什么__dict__Python 3中实例的大小如此之小?
Python使用魔术方法做了很多,其中大部分是一些协议的一部分.我熟悉"迭代器协议"和"数字协议",但最近偶然发现术语"序列协议".但即使经过一些研究,我也不确定"序列协议"是什么.
例如,C API函数PySequence_Check检查(根据文档)某个对象是否实现了"序列协议".该源代码表明这是一类,这不是一个字典,但实现__getitem__它的方法大致相同,在什么文件iter还指出:
[...]必须支持序列协议(
__getitem__()整数参数从0开始的方法).[...]
但是开始的要求0不是"实施"的PySequence_Check.
然后还有的collections.abc.Sequence类型,它基本上是说实例必须实现__reversed__,__contains__,__iter__和__len__.
但是根据该定义,实现"序列协议"的类不一定是序列,例如序列具有长度的"数据模型"和抽象类garantuee.但是实现__getitem__(传递PySequence_Check)的类在使用时会抛出异常len(an_instance_of_that_class).
有人可以告诉我序列和序列协议之间的区别(如果除了阅读源代码之外还有协议的定义)以及何时使用哪个定义?
首先,我理解一般来说装饰工作是如何工作的.而且我知道@staticmethod在签名中剥离实例参数
class C(object):
@staticmethod
def foo():
print 'foo'
C.foo //<function foo at 0x10efd4050>
C().foo //<function foo at 0x10efd4050>
Run Code Online (Sandbox Code Playgroud)
有效.
但是,我不明白如何staticmethod实现这一点的源代码.
在我看来,该包装方法时foo的staticmethod,实例staticmethod被实例化,那么一些神奇的发生,使C.foo()合法的.
那么......那些魔法会发生什么?做了staticmethod什么?
我知道关于SO的巨大话题,staticmethods但没有一个能解决我的疑虑.但也许我没有点击魔术关键词.如果是的话,请告诉我.
对于寻找staticmethod源代码的人,请参阅https://hg.python.org/cpython/file/c6880edaf6f3/Objects/funcobject.c
python static-methods decorator python-internals python-decorators
有没有什么理由x == x不能快速评估?我希望__eq__能检查它的两个参数是否相同,如果是,则立即返回True.但它没有这样做:
s = set(range(100000000))
s == s # this doesn't short-circuit, so takes ~1 sec
Run Code Online (Sandbox Code Playgroud)
对于内置插件,x == x总是返回True我认为?对于用户定义的类,我想有人可能会定义__eq__不满足此属性,但是有没有合理的用例呢?
我想之所以x == x能够快速评估是因为它是在一个巨大的性能损失非常大的参数memoizing功能:
from functools import lru_cache
@lru_cache()
def f(s):
return sum(s)
large_obj = frozenset(range(50000000))
f(large_obj) # this takes >1 sec every time
Run Code Online (Sandbox Code Playgroud)
请注意,@ lru_cache 对大型对象反复慢的原因并不是因为它需要计算__hash__(这只是执行一次然后由@jsbueno 指出的硬缓存),但因为__eq__ 每次都需要执行字典的哈希表确保它在桶中找到正确的对象(哈希的相等性显然是不够的).
更新:
对于三种情况,似乎值得分别考虑这个问题.
1)用户定义的类型(即,不是内置/标准库).
正如@donkopotamus指出的那样,有些情况下x == x不应该评估为True.例如,for numpy.array和pandas.Seriestypes,结果有意无法转换为boolean,因为它不清楚自然语义应该是什么(False意味着容器是空的,还是意味着它中的所有项都是False?).
但是在这里,python不需要做任何事情,因为 …
经过Mark Shannon对Python对象的优化,普通对象和带槽的对象有什么不同吗?据我所知,在正常用例中进行此优化后,对象没有字典。新的 Python 对象是否已经完全不需要使用槽了?
python ×10
python-internals ×10
class ×2
python-2.7 ×2
python-3.x ×2
comparison ×1
cpython ×1
decorator ×1
dictionary ×1
internals ×1
python-3.11 ×1
sequence ×1
slots ×1