3

在python中,字典的key可以是任意immutable对象,但json的key却只能是string。

在stackoverflow上搜到的相关问题,Why JSON allows only string to be a key?

最佳答案是说json是为了数据在不同程序之间相互传递,所以string能保证不同的程序语言都能支持这种数据结构。但我还是不明白为什么int、float之类的不行。

json.dumps({1:1,2:2})
'{"1": 1, "2": 2}
2016-06-09 提问

查看全部 7 个回答

10

你好,針對你的問題,我覺得你自己貼的那篇 stack overflow 的文章已經說得很清楚了。

補充一下我對於 json 中 object key 的看法:

首先你可以看到 json.org 中對於 json objectstring 的定義:

object: An object is an unordered set of name/value pairs. An object begins with { (left brace) and ends with } (right brace). Each name is followed by : (colon) and the name/value pairs are separated by , (comma).

string: A string is a sequence of zero or more Unicode characters, wrapped in double quotes, using backslash escapes. A character is represented as a single character string. A string is very much like a C or Java string.

同時我們可以在 The application/json Media Type for JavaScript Object Notation (JSON) 中看到以下的敘述:

An object is an unordered collection of zero or more name/value pairs, where a name is a string and a value is a string, number, boolean, null, object, or array.

我們可以清楚了解到,json 用來表述 object 用 string (雙引號夾住的字符集) 當作 key 的唯一選擇。

這說明了在 json 的定義或是 spec 中是明顯規範了 key 的型態跟表達方式,這可能出於某些設計哲學上的考量,也可能只是某些實作上的選擇,當然我們可以去追究當中的意涵,但是:

對於一般的使用者而言,能夠認識到這一點以避免設計上的問題應該是更為重要的。

就像是我們未必會去深究 Python 為何不使用 else if 而要使用 elif,為什麼我們複製 tuple 卻拿不到副本,而是拿到同一個對象等等。


當然這可能不能滿足我們的求知欲,所以我試圖提出一些觀點(還有真正大師的觀點)

P.S. 我在這裡並不打算說服你,因為我自認為我完全不是個 json 專家(笑)。

如果你嘗試使用過 javascript 去建立一個 object,你會發現,以下的敘述語句是行得通的:

var obj = {hello:1, world:2}

這相等於:

var obj = {"hello":1, "world":2}

可見在 js 中,object 的 key 有沒有雙引號在 evaluate 的時候都是一樣的。 甚至目前的 js object 用整數或是 js 關鍵字也能當作 key ...

但在 json 中,key 不但要是 string,還必須以雙引號夾住,所以很多人的疑問反而是,為什麼 js 中可以不用雙引號,但是 json 卻要? 我覺得這是一個重點,當雙引號的規則被使用的時候,不是 string 的東西也會變成 string 了(因為非得用雙引號不可),那所以為什麼要用雙引號咧?

關於這一點,你可以參考一下 Douglas Crockford (json 標準創造者) 在某個演講上面的一段說詞:

That was when we discovered the unquoted name problem. It turns out ECMA Script 3 has a whack reserved word policy. Reserved words must be quoted in the key position, which is really a nuisance. When I got around to formulizing this into a standard, I didn't want to have to put all of the reserved words in the standard, because it would look really stupid.

At the time, I was trying to convince people: yeah, you can write applications in JavaScript, it's actually going to work and it's a good language. I didn't want to say, then, at the same time: and look at this really stupid thing they did! So I decided, instead, let's just quote the keys. That way, we don't have to tell anybody about how whack it is. That's why, to this day, keys are quoted in JSON.

總而言之,大意是說,為了避免 json 因為要避免使用它的語言發生問題 所導致的過度複雜的標準(比如說考慮所有關於那個語言的關鍵字在作為 key 的情況),那乾脆用個簡單的法則來解決所有可能的意外,那就是加上雙引號拉。

以上不知道有沒有讓你有一些想法呢? 其實很多人覺得只能使用 string key 是為了不同語言使用 json 上面的穩定性我覺得也很有道理,我覺得能找個理由說服自己就很不錯了,畢竟怎麼理解他這樣設計可以有千千萬萬種解讀,至於真正的原因,也就只有他的創造者或標準的維護者知道了。


總之,簡單而明確的規則不是很不錯嗎!

參考資料:
in JSON, Why is each name quoted?
Is there any practical reason to use quoted strings for JSON keys?
Converting a JSON Text to a JavaScript Object
Object Equality in JavaScript


(以下是題外話)

對於你一開始的這個敘述:

在 Python 中,字典的 key 可以是任意 immutable 对象

稍微有點不精確,分享我自己的看法。

關於甚麼樣的對象可以作為字典的鍵,我們應該這樣說:

在 Python 中,字典的 key 必須要是 hashable 的

有人可能會覺得很奇怪,immutable 的對象不應該都是 hashable 的嗎?

其實並不盡然,我們看一個例子:

>>> a = ([1, 2, 3], 4, 5)
>>> hash(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
>>> dic = {}
>>> dic[a] = 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

a 是一個 tuple,我想我們應該都同意他是 immutable 的對象,可是這個 tuple 含有一個 mutable 的 list 元素,導致他自己成為一個非 hashable 對象,這就不能當作字典 dic 的鍵了。

這是非常容易被忽略的一點:

tuple 只有在他的元素都是 hashable 的情況下,才會是 hashable 的

所以其實有些 tuple 可以當作字典的鍵:

>>> b = (1,2,3)
>>> dic[b] = 0

那我們到底要如何判斷一個對象是否為 hashable 的(也就是他可以作為字典的鍵)呢? (第三點針對 Python3)

  1. 基本的 immutable builtin type 都是 hashable 的,除非他們包含了 mutable 的元素(見規則2),ex: str, bytes, ...

  2. tuple 只有在所有元素都是 hashable 的對象時,他才是 hashable 的

  3. 自定義的類基本上都是 hashable 的,因為他們的 hash 值是由他們的 id 值推導出來的。但假如我們有實作它的 __eq__ 方法,因為要維持 hash 跟 equality 的一致性,所以如果我們沒有一併重新實作 __hash__,則該類也會變成 unhashable 的。

以上幾點可參考:

  1. Types that define __eq__ are unhashable in Python 3.x?

  2. Python doc - Glossary

推荐答案

1

已采纳

首先JSON是Javascript的子集,而在Javascript中只有字符串、整型等5个数据类型,而后在其他编程语言中被使用,其他的语言的实现都是为了兼容JSON,比如Python标准库的json模块。详情可以查看json
至于JSON中为什么采用字符串作为键主要在于,首先需要是1个不可变的对象,因此只能是字符串、整型、浮点型这样的数据类型了,而在计算机中1个整型需要占用4个字节的内存,而字符只占用1个字节。
另外,使用JSON主要是为了传输数据,这个网络传输的过程中会丢失精度也是需要考虑的,而整型和浮点数都会出现精度的丢失。
另外使用字符串的形式还可以使用压缩算法进行压缩,进而节省带宽。
因此字符串是最佳的选择。

1

不好意思,剛剛仔細地想了一下你的回答,覺得好像哪裡怪怪的,想跟你討論看看.
json 雖然可以表達多種數據類型,但他本身就是個純文字文件,即便是表達整數或浮點數用的也都是"字串"(從文件檔案的觀點上來看,而不是他表達的數據類型來看),所以用來表達整數或浮點數的字串並不會多占內存(反而用以表示字串的字串多了兩個雙引號,可能佔用的空間更大),當然也不會丟失精準度(因為寫多少就是多少,轉換成 json 的時候可能丟失精準,因為要轉為字串,但是網路傳輸是不會丟失精準的).當然如果從以上的角度來看,壓縮也是沒問題的(如果可以壓縮的話).

dokelung · 2016年06月11日

添加评论

推广链接