ทำไมหลาม dict.update () ไม่คืนวัตถุ?


139

ฉันพยายามทำ:

award_dict = {
    "url" : "http://facebook.com",
    "imageurl" : "http://farm4.static.flickr.com/3431/3939267074_feb9eb19b1_o.png",
    "count" : 1,
}

def award(name, count, points, desc_string, my_size, parent) :
    if my_size > count :
        a = {
            "name" : name,
            "description" : desc_string % count,
            "points" : points,
            "parent_award" : parent,
        }
        a.update(award_dict)
        return self.add_award(a, siteAlias, alias).award

แต่ถ้ารู้สึกว่าฟังก์ชั่นยุ่งยากจริงๆและฉันจะทำค่อนข้าง:

        return self.add_award({
            "name" : name,
            "description" : desc_string % count,
            "points" : points,
            "parent_award" : parent,
        }.update(award_dict), siteAlias, alias).award

เหตุใดการอัปเดตจึงไม่ส่งคืนวัตถุเพื่อให้คุณเชื่อมโยงได้

JQuery ทำเช่นนี้เพื่อผูกมัด ทำไมมันไม่เป็นที่ยอมรับในหลาม?


14
* TL; DRnewdict = dict(dict001, **dict002)
dreftymac

2
@dreftymac ที่ใช้ไม่ได้กับความเข้าใจ
alancalvitti

@alancalvitti ใช่นั่นเป็นข้อแม้ที่ถูกต้องหนึ่งข้อที่จะชี้ให้เห็น
dreftymac

คำตอบ:


219

Python ส่วนใหญ่จะใช้การแยกคำสั่งการสืบค้นด้วยคำสั่ง : การNoneทำให้เกิดการเปลี่ยนแปลง (ซึ่งมีข้อยกเว้นในทางปฏิบัติเช่นpop;-) ทำให้พวกเขาไม่สามารถสับสนกับ accessors ได้ (และในหลอดเลือดดำเดียวกันการมอบหมายไม่ใช่การแสดงออก - การแยกแสดงออกอยู่ที่นั่นและอื่น ๆ )

นั่นไม่ได้หมายความว่ามีหลายวิธีที่จะรวมสิ่งต่าง ๆ เมื่อคุณต้องการเช่นdict(a, **award_dict)สร้าง dict ใหม่เหมือนที่คุณต้องการ.updateส่งคืน - ดังนั้นทำไมไม่ใช้สิ่งนั้นถ้าคุณรู้สึกว่ามันสำคัญ ?

แก้ไข : btw ไม่จำเป็นต้องใช้ในกรณีเฉพาะของคุณเพื่อสร้างaไปพร้อมกัน:

dict(name=name, description=desc % count, points=points, parent_award=parent,
     **award_dict)

สร้าง dict เดียวที่มีความหมายเหมือนกันกับของคุณa.update(award_dict)(รวมถึงในกรณีที่มีข้อขัดแย้งความจริงที่ว่ารายการในการaward_dictแทนที่สิ่งที่คุณให้อย่างชัดเจนเพื่อให้ได้ความหมายอื่น ๆ เพื่อให้ได้ความหมายอื่น ๆ ผ่านaward_dictเป็นARG ตำแหน่งแต่เพียงผู้เดียวก่อนที่คำหลักและสูญเสีย**รูปแบบ - dict(award_dict, name=nameฯลฯ ฯลฯ )


นั่นจะสร้างพจนานุกรมอีกฉบับหลังจากฉันสร้าง ฉันต้องการสร้าง dict จากนั้นเพิ่มค่าอื่น ๆ อีกมากมายแล้วมอบให้กับฟังก์ชัน
Paul Tarjan

@ พอลและนั่นคือสิ่งที่คุณกำลังทำอยู่ - มีสองข้อความ (อ่านได้มากกว่าวิธีซ้อนกันที่คุณต้องการ) ซึ่งสำหรับคุณ "รู้สึกยุ่งยากจริงๆ" การแก้ไขคำตอบของฉันจะแสดงวิธีการหลีกเลี่ยงการสร้างaทั้งหมด BTW,
อเล็กซ์เทล

1
โซลูชันดั้งเดิมไม่แข็งแกร่ง หาก award_dict มีกุญแจที่ระบุไว้แล้วไวยากรณ์จะถูกโยนทิ้งสำหรับอาร์กิวเมนต์คำหลักที่ซ้ำกัน โซลูชันของ jamylak dict (itertools.chain (d1.iteritems (), .. d <n> .iteritems ())) ไม่เพียงทำงานในกรณีที่พจนานุกรมมีคีย์ที่ซ้ำกัน แต่ยังช่วยให้คุณสามารถรวมพจนานุกรมหลาย ๆ อันในภายหลังด้วย ห่วงโซ่มีความสำคัญสำหรับค่าสุดท้าย
แมตต์

2
นอกจากนี้หากคีย์ใน award_dict ไม่เป็นสตริงล่ามจะโยน aTypeError
kunl

3
dict(old_dict, old_key=new_value)จะไม่โยนค่าหลายคำสำหรับคำหลักและส่งคืน dict ใหม่
Charmy

35

Python API โดยการประชุมแยกความแตกต่างระหว่างขั้นตอนและฟังก์ชั่น ฟังก์ชันคำนวณค่าใหม่จากพารามิเตอร์ (รวมถึงวัตถุเป้าหมายใด ๆ ) ขั้นตอนการปรับเปลี่ยนวัตถุและไม่ส่งคืนสิ่งใด (เช่นพวกเขากลับไม่มี) ดังนั้นขั้นตอนมีผลข้างเคียง, ฟังก์ชั่นไม่ได้ update เป็นขั้นตอนดังนั้นจึงไม่ส่งคืนค่า

แรงจูงใจในการทำเช่นนั้นคือไม่เช่นนั้นคุณอาจได้รับผลข้างเคียงที่ไม่พึงประสงค์ พิจารณา

bar = foo.reverse()

หากย้อนกลับ (ซึ่งกลับรายการในตำแหน่งเดิม) ก็จะส่งคืนรายการด้วยผู้ใช้อาจคิดว่าการย้อนกลับส่งคืนรายการใหม่ที่ได้รับการกำหนดให้กับแถบและไม่เคยสังเกตเห็นว่า foo ได้รับการแก้ไขด้วย ด้วยการย้อนกลับไม่มีไม่มีพวกเขารับรู้ได้ทันทีว่าแถบไม่ใช่ผลของการกลับรายการและจะมองใกล้มากขึ้นว่าผลของการย้อนกลับนั้น


1
ขอบคุณ. ทำไมจะไม่ย้อนกลับยังให้ตัวเลือกที่จะไม่ทำในสถานที่? ประสิทธิภาพ? ทำreverse(foo)รู้สึกแปลก ๆ
Paul Tarjan

การเพิ่มตัวเลือกจะไม่เหมาะสม: มันจะเปลี่ยนลักษณะของวิธีการขึ้นอยู่กับพารามิเตอร์ อย่างไรก็ตามวิธีการควรมีประเภทการคืนค่าคงที่ (มีกรณีที่กฎนี้เสีย) ง่ายต่อการสร้างสำเนาย้อนกลับ: เพียงทำสำเนา (โดยใช้bar=foo[:]) จากนั้นเปลี่ยนกลับเป็นสำเนา
Martin v. Löwis

3
ฉันคิดว่าเหตุผลนั้นชัดเจน ในbar = foo.reverse()คุณอาจคิดว่าfooไม่มีการแก้ไข ไปสู่ความสับสนหลีกเลี่ยงคุณมีทั้งและfoo.reverse() bar = reversed(foo)
Roberto Bonvallet

เกิดอะไรขึ้นกับการเปลี่ยนลักษณะของพารามิเตอร์ตามพารามิเตอร์
Julien


15
>>> dict_merge = lambda a,b: a.update(b) or a
>>> dict_merge({'a':1, 'b':3},{'c':5})
{'a': 1, 'c': 5, 'b': 3}

โปรดทราบว่าเช่นเดียวกับการคืนค่า dict ที่ผสานมันจะแก้ไขพารามิเตอร์แรกแทน ดังนั้น dict_merge (a, b) จะแก้ไข a

หรือแน่นอนคุณสามารถทำแบบอินไลน์ได้ทั้งหมด:

>>> (lambda a,b: a.update(b) or a)({'a':1, 'b':3},{'c':5})
{'a': 1, 'c': 5, 'b': 3}

10
lambdaไม่ควรใช้-1 เช่นนั้นแทนที่จะใช้ฟังก์ชั่นทั่วไปdefแทน
jamylak

8
ไม่จำเป็นต้องใช้แลมบ์ดาเพียงใช้a.update(b) or a
Pycz

10

ชื่อเสียงไม่เพียงพอสำหรับความคิดเห็นที่เหลืออยู่บนคำตอบด้านบน

@beardc ดูเหมือนว่านี่จะไม่ใช่สิ่งที่ CPython PyPy ให้ฉัน "TypeError: คำหลักจะต้องเป็นสตริง"

วิธีการแก้ปัญหาที่มี**kwargsเพียงทำงานเพราะพจนานุกรมที่จะรวมมีเพียงคีย์ของประเภทสตริง

กล่าวคือ

>>> dict({1:2}, **{3:4})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: keyword arguments must be strings

VS

>>> dict({1:2}, **{'3':4})
{1: 2, '3': 4}

5

ไม่ใช่ว่ามันจะไม่ยอมรับ แต่มันdictsก็ไม่ได้ถูกนำไปใช้ในทางนั้น

ถ้าคุณดูที่ ORM ของ Django มันจะใช้ประโยชน์จากการผูกมัดอย่างกว้างขวาง มันไม่ท้อแท้คุณสามารถสืบทอดdictและแทนที่เฉพาะupdateเพื่อทำการอัปเดตและreturn selfถ้าคุณต้องการมันจริงๆ

class myDict(dict):
    def update(self, *args):
        dict.update(self, *args)
        return self

ขอบคุณนี่อาจเป็นแพทช์ดิชฉันแค่อยากรู้ว่าทำไม dict () จึงไม่อนุญาตให้ใช้ฟังก์ชันนี้ (เพราะมันง่ายอย่างที่คุณสาธิต) Django patch เป็นแบบนี้หรือเปล่า?
Paul Tarjan

2

ใกล้เคียงกับโซลูชันที่คุณเสนอเท่าที่จะทำได้

from collections import ChainMap

return self.add_award(ChainMap(award_dict, {
    "name" : name,
    "description" : desc_string % count,
    "points" : points,
    "parent_award" : parent,
}), siteAlias, alias).award

1

สำหรับผู้ที่มาปาร์ตี้ฉันมีเวลา (Py 3.7) แสดงให้เห็นว่า.update()วิธีการพื้นฐานดูเร็วขึ้นเล็กน้อย (~ 5%) เมื่ออินพุตถูกเก็บรักษาไว้และสังเกตเห็นได้ชัดขึ้น (~ 30%) เร็วขึ้นเมื่ออัปเดตแบบแทน .

ตามปกติมาตรฐานทั้งหมดควรได้รับเม็ดเกลือ

def join2(dict1, dict2, inplace=False):
    result = dict1 if inplace else dict1.copy()
    result.update(dict2)
    return result


def join(*items):
    iter_items = iter(items)
    result = next(iter_items).copy()
    for item in iter_items:
        result.update(item)
    return result


def update_or(dict1, dict2):
    return dict1.update(dict2) or dict1


d1 = {i: str(i) for i in range(1000000)}
d2 = {str(i): i for i in range(1000000)}

%timeit join2(d1, d2)
# 258 ms ± 1.47 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit join(d1, d2)
# 262 ms ± 2.97 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit dict(d1, **d2)
# 267 ms ± 2.74 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit {**d1, **d2}
# 267 ms ± 1.84 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

การกำหนดเวลาสำหรับการดำเนินการในสถานที่นั้นค่อนข้างยุ่งยากดังนั้นจึงจำเป็นต้องแก้ไขพร้อมการดำเนินการคัดลอกเพิ่มเติม (ช่วงเวลาแรกเป็นเพียงการอ้างอิง):

%timeit dd = d1.copy()
# 44.9 ms ± 495 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit dd = d1.copy(); join2(dd, d2)
# 296 ms ± 2.05 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit dd = d1.copy(); join2(dd, d2, True)
# 234 ms ± 1.02 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit dd = d1.copy(); update_or(dd, d2)
# 235 ms ± 1.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)


0

เพิ่งลองทำสิ่งนี้ด้วยตัวเองใน Python 3.4 (ดังนั้นจึงไม่สามารถใช้{**dict_1, **dict_2}รูปแบบแฟนซีได้

ฉันต้องการที่จะมีคีย์ที่ไม่เป็นสตริงในพจนานุกรมเช่นเดียวกับการจัดทำพจนานุกรมตามอำเภอใจ

นอกจากนี้ฉันต้องการสร้างพจนานุกรมใหม่ดังนั้นฉันจึงเลือกที่จะไม่ใช้collections.ChainMap(เป็นเหตุผลที่ฉันไม่ต้องการใช้dict.updateในตอนแรก

นี่คือสิ่งที่ฉันสิ้นสุดการเขียน:

def merge_dicts(*dicts):
    all_keys  = set(k for d in dicts for k in d.keys())
    chain_map = ChainMap(*reversed(dicts))
    return {k: chain_map[k] for k in all_keys}

merge_maps({'1': 1}, {'2': 2, '3': 3}, {'1': 4, '3': 5})
# {'1': 4, '3': 5, '2': 2}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.