การมีอยู่ของทูเปิลที่ไม่เปลี่ยนชื่อใน Python?


121

ใครสามารถแก้ไขnametupleหรือจัดเตรียมคลาสทางเลือกเพื่อให้ทำงานกับวัตถุที่เปลี่ยนแปลงได้?

เพื่อความสามารถในการอ่านเป็นหลักฉันต้องการสิ่งที่คล้ายกับ namedtuple ที่ทำสิ่งนี้:

from Camelot import namedgroup

Point = namedgroup('Point', ['x', 'y'])
p = Point(0, 0)
p.x = 10

>>> p
Point(x=10, y=0)

>>> p.x *= 10
Point(x=100, y=0)

ต้องเป็นไปได้ที่จะดองวัตถุที่เป็นผลลัพธ์ และตามลักษณะของชื่อทูเปิลลำดับของเอาต์พุตเมื่อแสดงจะต้องตรงกับลำดับของรายการพารามิเตอร์เมื่อสร้างวัตถุ


3
ดูเพิ่มเติม: stackoverflow.com/q/5131044 มีเหตุผลที่คุณไม่สามารถใช้พจนานุกรมได้หรือไม่?
senshin

@senshin ขอบคุณสำหรับลิงค์ ฉันไม่ต้องการใช้พจนานุกรมด้วยเหตุผลที่ระบุไว้ในนั้น คำตอบนั้นเชื่อมโยงกับcode.activestate.com/recipes/…ซึ่งค่อนข้างใกล้เคียงกับสิ่งที่ฉันตามมา
Alexander

ซึ่งแตกต่างจากnamedtuples ดูเหมือนว่าคุณไม่จำเป็นต้องสามารถอ้างอิงแอตทริบิวต์ด้วยดัชนีกล่าวคือเช่นนั้นp[0]และp[1]จะเป็นวิธีอื่นในการอ้างอิงxและyตามลำดับถูกต้องหรือไม่?
martineau

ตามหลักการแล้วใช่จัดทำดัชนีได้ตามตำแหน่งเช่นทูเพิลธรรมดานอกเหนือจากชื่อและคลายออกเหมือนทูเปิล สูตร ActiveState นี้ใกล้เคียง แต่ฉันเชื่อว่ามันใช้พจนานุกรมปกติแทน OrderDict code.activestate.com/recipes/500261
Alexander

2
Nametuple ที่ไม่แน่นอนเรียกว่าคลาส
gbtimmon

คำตอบ:


133

มีทางเลือกที่ไม่แน่นอนคือการcollections.namedtuple- recordclass

มี API และหน่วยความจำเหมือนกันnamedtupleและรองรับการมอบหมายงาน (ควรเร็วกว่าด้วย) ตัวอย่างเช่น:

from recordclass import recordclass

Point = recordclass('Point', 'x y')

>>> p = Point(1, 2)
>>> p
Point(x=1, y=2)
>>> print(p.x, p.y)
1 2
>>> p.x += 2; p.y += 3; print(p)
Point(x=3, y=5)

สำหรับ python 3.6 และสูงกว่าrecordclass(ตั้งแต่ 0.5) การสนับสนุน typehints:

from recordclass import recordclass, RecordClass

class Point(RecordClass):
   x: int
   y: int

>>> Point.__annotations__
{'x':int, 'y':int}
>>> p = Point(1, 2)
>>> p
Point(x=1, y=2)
>>> print(p.x, p.y)
1 2
>>> p.x += 2; p.y += 3; print(p)
Point(x=3, y=5)

มีตัวอย่างที่สมบูรณ์มากขึ้น(รวมถึงการเปรียบเทียบประสิทธิภาพด้วย)

เนื่องจากrecordclassไลบรารี0.9 มีตัวแปรอื่น - recordclass.structclassฟังก์ชันโรงงาน สามารถสร้างคลาสซึ่งอินสแตนซ์ใช้หน่วยความจำน้อยกว่า__slots__อินสแตนซ์พื้นฐาน สิ่งนี้มีความสำคัญสำหรับอินสแตนซ์ที่มีค่าแอตทริบิวต์ซึ่งไม่ได้ตั้งใจให้มีรอบการอ้างอิง อาจช่วยลดการใช้หน่วยความจำหากคุณต้องการสร้างอินสแตนซ์หลายล้านอินสแตนซ์ นี่คือตัวอย่างเช่น


4
ชอบมัน. 'ห้องสมุดนี้เป็น "หลักฐานแนวคิด" สำหรับปัญหาของทางเลือกที่ "ไม่แน่นอน" ของทูเปิลที่มีชื่อ "
Alexander

1
recordclassช้ากว่าใช้หน่วยความจำมากขึ้นและต้องใช้นามสกุล C เมื่อเทียบกับสูตรของ Antti Haapala และnamedlist.
GrantJ

recordclassเป็นเวอร์ชันที่ไม่แน่นอนcollection.namedtupleซึ่งสืบทอดมาเป็น API รอยเท้าหน่วยความจำ แต่รองรับการมอบหมายงาน namedlistเป็นตัวอย่างของคลาส python ที่มีสล็อต จะมีประโยชน์มากขึ้นหากคุณไม่ต้องการเข้าถึงฟิลด์โดยใช้ดัชนีอย่างรวดเร็ว
intellimath

การเข้าถึงแอตทริบิวต์recordclassเช่น (python 3.5.2) ช้ากว่าสำหรับnamedlist
intellimath

เมื่อใช้namedtupleและการสร้างคลาสอย่างง่ายPoint = namedtuple('Point', 'x y')เจไดสามารถเติมคุณสมบัติอัตโนมัติได้ในขณะที่ไม่เป็นเช่นrecordclassนั้น ถ้าฉันใช้รหัสการสร้างที่ยาวกว่า (ตามRecordClass) เจไดจะเข้าใจPointคลาส แต่ไม่ใช่ตัวสร้างหรือคุณสมบัติ ... มีวิธีที่recordclassจะทำงานร่วมกับเจไดอย่างดีได้หรือไม่?
PhilMacKay

34

types.SimpleNamespaceถูกนำมาใช้ใน Python 3.3 และรองรับข้อกำหนดที่ร้องขอ

from types import SimpleNamespace
t = SimpleNamespace(foo='bar')
t.ham = 'spam'
print(t)
namespace(foo='bar', ham='spam')
print(t.foo)
'bar'
import pickle
with open('/tmp/pickle', 'wb') as f:
    pickle.dump(t, f)

1
ฉันมองหาอะไรแบบนี้มาหลายปีแล้ว เปลี่ยนไลบรารี dotted dict อย่าง dotmap ได้อย่างยอดเยี่ยม
axwell

1
สิ่งนี้ต้องการการโหวตเพิ่มขึ้น มันคือสิ่งที่ OP กำลังมองหาอยู่ในไลบรารีมาตรฐานและไม่สามารถใช้งานได้ง่ายกว่านี้ ขอบคุณ!
Tom Zych

3
-1 OP ทำให้ชัดเจนมากกับการทดสอบของเขาว่าเขาต้องการอะไรและSimpleNamespaceไม่ผ่านการทดสอบ 6-10 (เข้าถึงโดยดัชนี, การแกะกล่องซ้ำ, การทำซ้ำ, คำสั่งตามคำสั่ง, การแทนที่ในสถานที่) และ 12, 13 (ช่อง, ช่อง) โปรดทราบว่าเอกสาร (ที่คุณเชื่อมโยงในคำตอบ) ระบุว่า" SimpleNamespaceอาจมีประโยชน์ในการใช้แทนclass NS: passอย่างไรก็ตามสำหรับประเภทระเบียนที่มีโครงสร้างให้ใช้namedtuple()แทน"
อาลี

1
-1 ด้วยเช่นกันSimpleNamespaceสร้างอ็อบเจ็กต์ไม่ใช่ตัวสร้างคลาสและไม่สามารถแทนที่ namedtuple ได้ การเปรียบเทียบประเภทจะไม่ทำงานและรอยเท้าหน่วยความจำจะสูงขึ้นมาก
RedGlyph

26

ในฐานะที่เป็นทางเลือก Pythonic สำหรับงานนี้เนื่องจาก Python-3.7 คุณสามารถใช้ dataclassesโมดูลที่ไม่เพียง แต่มีพฤติกรรมไม่แน่นอนNamedTupleเนื่องจากใช้นิยามคลาสปกติ แต่ยังรองรับคุณสมบัติคลาสอื่น ๆ

จาก PEP-0557:

แม้ว่าจะใช้กลไกที่แตกต่างกันมาก แต่ Data Classes อาจถูกคิดว่าเป็น "ชื่อที่เปลี่ยนแปลงได้โดยมีค่าเริ่มต้น" เนื่องจากคลาสข้อมูลใช้ไวยากรณ์นิยามคลาสปกติคุณจึงมีอิสระที่จะใช้การสืบทอด metaclasses docstrings เมธอดที่ผู้ใช้กำหนดเองโรงงานคลาสและคุณสมบัติคลาส Python อื่น ๆ

มีการจัดเตรียมมัณฑนากรชั้นเรียนซึ่งตรวจสอบนิยามคลาสสำหรับตัวแปรที่มีคำอธิบายประกอบประเภทตามที่กำหนดในPEP 526 "ไวยากรณ์สำหรับคำอธิบายประกอบตัวแปร" ในเอกสารนี้ตัวแปรดังกล่าวเรียกว่าเขตข้อมูล การใช้ฟิลด์เหล่านี้มัณฑนากรจะเพิ่มนิยามเมธอดที่สร้างขึ้นให้กับคลาสเพื่อรองรับการเริ่มต้นอินสแตนซ์การทำซ้ำวิธีการเปรียบเทียบและวิธีอื่น ๆ ที่เป็นทางเลือกตามที่อธิบายไว้ในส่วนข้อกำหนด คลาสดังกล่าวเรียกว่าคลาสข้อมูล แต่ไม่มีอะไรพิเศษเกี่ยวกับคลาส: มัณฑนากรเพิ่มเมธอดที่สร้างขึ้นให้กับคลาสและส่งคืนคลาสเดียวกันกับคลาสที่ได้รับ

คุณลักษณะนี้แนะนำในPEP-0557ซึ่งคุณสามารถอ่านรายละเอียดเพิ่มเติมได้ที่ลิงค์เอกสารที่ให้มา

ตัวอย่าง:

In [20]: from dataclasses import dataclass

In [21]: @dataclass
    ...: class InventoryItem:
    ...:     '''Class for keeping track of an item in inventory.'''
    ...:     name: str
    ...:     unit_price: float
    ...:     quantity_on_hand: int = 0
    ...: 
    ...:     def total_cost(self) -> float:
    ...:         return self.unit_price * self.quantity_on_hand
    ...:    

การสาธิต:

In [23]: II = InventoryItem('bisc', 2000)

In [24]: II
Out[24]: InventoryItem(name='bisc', unit_price=2000, quantity_on_hand=0)

In [25]: II.name = 'choco'

In [26]: II.name
Out[26]: 'choco'

In [27]: 

In [27]: II.unit_price *= 3

In [28]: II.unit_price
Out[28]: 6000

In [29]: II
Out[29]: InventoryItem(name='choco', unit_price=6000, quantity_on_hand=0)

1
เห็นได้ชัดมากกับการทดสอบใน OP สิ่งที่จำเป็นและdataclassล้มเหลวในการทดสอบ 6-10 (การเข้าถึงโดยดัชนี, การแกะกล่องซ้ำ, การทำซ้ำ, คำสั่งตามคำสั่ง, การแทนที่ในสถานที่) และ 12, 13 (ช่อง, ช่อง) ใน Python 3.7 0.1
อาลี

1
แม้ว่านี่อาจไม่ใช่สิ่งที่ OP กำลังมองหาโดยเฉพาะ แต่ก็ช่วยฉันได้อย่างแน่นอน :)
Martin CR

25

ล่าสุดnamedlist 1.7 ผ่านการทดสอบทั้งหมดของคุณกับทั้งงูหลาม 2.7 และ Python 3.5ณ วันที่ 11 ม.ค. 2016 มันเป็นการนำงูหลามบริสุทธิ์ในขณะที่recordclassเป็นส่วนขยายของ C แน่นอนขึ้นอยู่กับความต้องการของคุณว่าต้องการใช้นามสกุล C หรือไม่

การทดสอบของคุณ (แต่ดูหมายเหตุด้านล่างด้วย):

from __future__ import print_function
import pickle
import sys
from namedlist import namedlist

Point = namedlist('Point', 'x y')
p = Point(x=1, y=2)

print('1. Mutation of field values')
p.x *= 10
p.y += 10
print('p: {}, {}\n'.format(p.x, p.y))

print('2. String')
print('p: {}\n'.format(p))

print('3. Representation')
print(repr(p), '\n')

print('4. Sizeof')
print('size of p:', sys.getsizeof(p), '\n')

print('5. Access by name of field')
print('p: {}, {}\n'.format(p.x, p.y))

print('6. Access by index')
print('p: {}, {}\n'.format(p[0], p[1]))

print('7. Iterative unpacking')
x, y = p
print('p: {}, {}\n'.format(x, y))

print('8. Iteration')
print('p: {}\n'.format([v for v in p]))

print('9. Ordered Dict')
print('p: {}\n'.format(p._asdict()))

print('10. Inplace replacement (update?)')
p._update(x=100, y=200)
print('p: {}\n'.format(p))

print('11. Pickle and Unpickle')
pickled = pickle.dumps(p)
unpickled = pickle.loads(pickled)
assert p == unpickled
print('Pickled successfully\n')

print('12. Fields\n')
print('p: {}\n'.format(p._fields))

print('13. Slots')
print('p: {}\n'.format(p.__slots__))

เอาต์พุตบน Python 2.7

1. การกลายพันธุ์ของค่าฟิลด์  
p: 10, 12

2. สตริง  
p: จุด (x = 10, y = 12)

3. การเป็นตัวแทน  
จุด (x = 10, y = 12) 

4. ขนาดของ  
ขนาด p: 64 

5. เข้าถึงตามชื่อเขตข้อมูล  
p: 10, 12

6. เข้าถึงโดยดัชนี  
p: 10, 12

7. แกะกล่องซ้ำ  
p: 10, 12

8. การทำซ้ำ  
p: [10, 12]

9. สั่ง Dict  
p: OrderDict ([('x', 10), ('y', 12)])

10. การเปลี่ยนแทนที่ (อัพเดต?)  
p: จุด (x = 100, y = 200)

11. ดองและแกะ  
ดองสำเร็จ

12. ทุ่งนา  
p: ('x', 'y')

13. สล็อต  
p: ('x', 'y')

ข้อแตกต่างเพียงอย่างเดียวกับ Python 3.5 คือnamedlistมีขนาดเล็กลงคือ 56 (Python 2.7 รายงาน 64)

โปรดทราบว่าฉันได้เปลี่ยนการทดสอบ 10 ของคุณสำหรับการแทนที่แบบแทนที่ namedlistมี_replace()วิธีการที่ไม่สำเนาตื้นและที่ทำให้รู้สึกดีกับผมเพราะnamedtupleในห้องสมุดมาตรฐานพฤติกรรมในลักษณะเดียวกัน การเปลี่ยนความหมายของ_replace()วิธีการจะทำให้เกิดความสับสน ในความคิดของฉัน_update()ควรใช้วิธีการสำหรับการอัปเดตแบบแทนที่ หรือบางทีฉันอาจไม่เข้าใจเจตนาของการทดสอบ 10 ของคุณ?


มีความแตกต่างกันเล็กน้อยที่สำคัญ namedlistเก็บค่าในกรณีรายการ เป็นสิ่งที่cpython's listเป็นจริงอาร์เรย์แบบไดนามิก โดยการออกแบบจะจัดสรรหน่วยความจำมากเกินความจำเป็นเพื่อให้การกลายพันธุ์ของรายการถูกลง
intellimath

1
@intellimath ชื่อรายการเป็นบิตของการเรียกชื่อผิด มันไม่ได้สืบทอดจากlistและโดยค่าเริ่มต้นใช้__slots__การเพิ่มประสิทธิภาพ เมื่อฉันวัดการใช้หน่วยความจำน้อยกว่าrecordclass: 96 ไบต์เทียบกับ 104 ไบต์สำหรับหกฟิลด์บน Python 2.7
GrantJ

@GrantJ ครับ recorclassใช้หน่วยความจำมากขึ้นเนื่องจากเป็นtupleวัตถุเหมือนที่มีขนาดหน่วยความจำตัวแปร
intellimath

2
การโหวตแบบไม่เปิดเผยตัวไม่ได้ช่วยใคร มีอะไรผิดปกติกับคำตอบ? ทำไมต้องโหวตลง?
อาลี

types.SimpleNamespaceฉันรักความปลอดภัยกับความผิดพลาดที่จะให้ด้วยความเคารพ น่าเสียดายที่
ไพลินท์

23

ดูเหมือนว่าคำตอบสำหรับคำถามนี้คือไม่

ด้านล่างค่อนข้างใกล้ แต่ก็ไม่เปลี่ยนแปลงในทางเทคนิค นี่คือการสร้างnamedtuple()อินสแตนซ์ใหม่ที่มีค่า x ที่อัปเดต:

Point = namedtuple('Point', ['x', 'y'])
p = Point(0, 0)
p = p._replace(x=10) 

ในทางกลับกันคุณสามารถสร้างคลาสง่ายๆโดยใช้__slots__ซึ่งควรทำงานได้ดีสำหรับการอัปเดตแอตทริบิวต์อินสแตนซ์คลาสบ่อยๆ:

class Point:
    __slots__ = ['x', 'y']
    def __init__(self, x, y):
        self.x = x
        self.y = y

เพื่อเพิ่มคำตอบนี้ฉันคิดว่า__slots__ใช้ที่นี่ได้ดีเพราะหน่วยความจำมีประสิทธิภาพเมื่อคุณสร้างอินสแตนซ์คลาสจำนวนมาก ข้อเสียเพียงอย่างเดียวคือคุณไม่สามารถสร้างแอตทริบิวต์คลาสใหม่ได้

นี่คือหัวข้อที่เกี่ยวข้องซึ่งแสดงให้เห็นถึงประสิทธิภาพของหน่วยความจำ - Dictionary vs Object - ซึ่งมีประสิทธิภาพมากกว่าและทำไม

เนื้อหาที่ยกมาในคำตอบของเธรดนี้เป็นคำอธิบายที่รวบรัดมากว่าทำไม__slots__หน่วยความจำจึงมีประสิทธิภาพมากกว่า - สล็อต Python


1
ปิด แต่ clunky สมมติว่าฉันต้องการทำงานที่ได้รับมอบหมาย + = ฉันจะต้องทำ: p._replace (x = px + 10) เทียบกับ px + = 10
Alexander

1
ใช่มันไม่ได้เปลี่ยนทูเปิลที่มีอยู่จริงๆมันกำลังสร้างอินสแตนซ์ใหม่
kennes

8

ต่อไปนี้เป็นทางออกที่ดีสำหรับ Python 3: คลาสขั้นต่ำที่ใช้__slots__และSequenceคลาสฐานนามธรรม ไม่ได้ทำการตรวจจับข้อผิดพลาดแฟนซีหรือสิ่งนั้น แต่ใช้งานได้และทำงานส่วนใหญ่เหมือนทูเปิลที่ไม่แน่นอน (ยกเว้นการตรวจสอบการพิมพ์)

from collections import Sequence

class NamedMutableSequence(Sequence):
    __slots__ = ()

    def __init__(self, *a, **kw):
        slots = self.__slots__
        for k in slots:
            setattr(self, k, kw.get(k))

        if a:
            for k, v in zip(slots, a):
                setattr(self, k, v)

    def __str__(self):
        clsname = self.__class__.__name__
        values = ', '.join('%s=%r' % (k, getattr(self, k))
                           for k in self.__slots__)
        return '%s(%s)' % (clsname, values)

    __repr__ = __str__

    def __getitem__(self, item):
        return getattr(self, self.__slots__[item])

    def __setitem__(self, item, value):
        return setattr(self, self.__slots__[item], value)

    def __len__(self):
        return len(self.__slots__)

class Point(NamedMutableSequence):
    __slots__ = ('x', 'y')

ตัวอย่าง:

>>> p = Point(0, 0)
>>> p.x = 10
>>> p
Point(x=10, y=0)
>>> p.x *= 10
>>> p
Point(x=100, y=0)

หากต้องการคุณสามารถมีวิธีสร้างคลาสได้เช่นกัน (แม้ว่าการใช้คลาสที่ชัดเจนจะโปร่งใสกว่า):

def namedgroup(name, members):
    if isinstance(members, str):
        members = members.split()
    members = tuple(members)
    return type(name, (NamedMutableSequence,), {'__slots__': members})

ตัวอย่าง:

>>> Point = namedgroup('Point', ['x', 'y'])
>>> Point(6, 42)
Point(x=6, y=42)

ใน Python 2 คุณต้องปรับแต่งเล็กน้อย - หากคุณสืบทอดมาSequenceคลาสจะมี a__dict__และ__slots__จะหยุดทำงาน

วิธีแก้ปัญหาใน Python 2 คือการไม่สืบทอดจากSequenceแต่object. หากisinstance(Point, Sequence) == Trueต้องการคุณต้องลงทะเบียน NamedMutableSequenceเป็นคลาสพื้นฐานเพื่อSequence:

Sequence.register(NamedMutableSequence)

3

มาใช้สิ่งนี้กับการสร้างประเภทไดนามิก:

import copy
def namedgroup(typename, fieldnames):

    def init(self, **kwargs): 
        attrs = {k: None for k in self._attrs_}
        for k in kwargs:
            if k in self._attrs_:
                attrs[k] = kwargs[k]
            else:
                raise AttributeError('Invalid Field')
        self.__dict__.update(attrs)

    def getattribute(self, attr):
        if attr.startswith("_") or attr in self._attrs_:
            return object.__getattribute__(self, attr)
        else:
            raise AttributeError('Invalid Field')

    def setattr(self, attr, value):
        if attr in self._attrs_:
            object.__setattr__(self, attr, value)
        else:
            raise AttributeError('Invalid Field')

    def rep(self):
         d = ["{}={}".format(v,self.__dict__[v]) for v in self._attrs_]
         return self._typename_ + '(' + ', '.join(d) + ')'

    def iterate(self):
        for x in self._attrs_:
            yield self.__dict__[x]
        raise StopIteration()

    def setitem(self, *args, **kwargs):
        return self.__dict__.__setitem__(*args, **kwargs)

    def getitem(self, *args, **kwargs):
        return self.__dict__.__getitem__(*args, **kwargs)

    attrs = {"__init__": init,
                "__setattr__": setattr,
                "__getattribute__": getattribute,
                "_attrs_": copy.deepcopy(fieldnames),
                "_typename_": str(typename),
                "__str__": rep,
                "__repr__": rep,
                "__len__": lambda self: len(fieldnames),
                "__iter__": iterate,
                "__setitem__": setitem,
                "__getitem__": getitem,
                }

    return type(typename, (object,), attrs)

ตรวจสอบแอตทริบิวต์เพื่อดูว่าถูกต้องหรือไม่ก่อนที่จะอนุญาตให้ดำเนินการต่อ

ดองได้ไหม? ใช่ถ้า (และเฉพาะในกรณีที่) คุณทำสิ่งต่อไปนี้:

>>> import pickle
>>> Point = namedgroup("Point", ["x", "y"])
>>> p = Point(x=100, y=200)
>>> p2 = pickle.loads(pickle.dumps(p))
>>> p2.x
100
>>> p2.y
200
>>> id(p) != id(p2)
True

คำจำกัดความจะต้องอยู่ในเนมสเปซของคุณและต้องมีอยู่นานพอที่ดองจะพบ ดังนั้นหากคุณกำหนดสิ่งนี้ให้อยู่ในแพ็กเกจของคุณก็น่าจะใช้ได้

Point = namedgroup("Point", ["x", "y"])

Pickle จะล้มเหลวหากคุณทำสิ่งต่อไปนี้หรือกำหนดนิยามชั่วคราว (ออกไปนอกขอบเขตเมื่อฟังก์ชันสิ้นสุดลงพูด):

some_point = namedgroup("Point", ["x", "y"])

และใช่มันจะรักษาลำดับของฟิลด์ที่แสดงรายการในการสร้างชนิด


หากคุณเพิ่ม__iter__วิธีการด้วยfor k in self._attrs_: yield getattr(self, k)สิ่งนั้นจะรองรับการแกะกล่องเหมือนทูเปิล
snapshoe

นอกจากนี้ยังสวยง่ายต่อการเพิ่ม__len__, __getitem__และ__setiem__วิธีการที่จะได้รับการสนับสนุนโดยดัชนี valus p[0]เช่น ด้วยบิตสุดท้ายเหล่านี้ดูเหมือนว่าจะเป็นคำตอบที่สมบูรณ์และถูกต้องที่สุด (สำหรับฉันแล้ว)
snapshoe

__len__และ__iter__เป็นสิ่งที่ดี __getitem__และ__setitem__สามารถจริงๆจะแมปไปself.__dict__.__setitem__และself.__dict__.__getitem__
MadMan2064

2

Tuples เป็นไปตามความหมายที่ไม่เปลี่ยนรูป

อย่างไรก็ตามคุณสามารถสร้างคลาสย่อยของพจนานุกรมที่คุณสามารถเข้าถึงแอตทริบิวต์ด้วยเครื่องหมายจุด

In [1]: %cpaste
Pasting code; enter '--' alone on the line to stop or use Ctrl-D.
:class AttrDict(dict):
:
:    def __getattr__(self, name):
:        return self[name]
:
:    def __setattr__(self, name, value):
:        self[name] = value
:--

In [2]: test = AttrDict()

In [3]: test.a = 1

In [4]: test.b = True

In [5]: test
Out[5]: {'a': 1, 'b': True}

2

หากคุณต้องการลักษณะการทำงานที่คล้ายกับชื่อรายการ แต่ไม่สามารถเปลี่ยนแปลงได้ให้ลองชื่อรายการ

โปรดทราบว่าเพื่อให้สามารถเปลี่ยนแปลงได้จะต้องไม่เป็นทูเพิล


ขอบคุณสำหรับลิงค์ ดูเหมือนว่าจะใกล้เคียงที่สุด แต่ฉันต้องประเมินรายละเอียดเพิ่มเติม Btw ฉันรู้ดีว่าสิ่งที่ไม่เปลี่ยนรูปนั้นไม่เปลี่ยนรูปซึ่งเป็นสาเหตุที่ฉันกำลังมองหาวิธีแก้ปัญหาเช่น namedtuple
Alexander

0

ประสิทธิภาพที่ให้มานั้นมีความสำคัญเพียงเล็กน้อยเราสามารถใช้แฮ็คโง่ ๆ เช่น:

from collection import namedtuple

Point = namedtuple('Point', 'x y z')
mutable_z = Point(1,2,[3])

1
คำตอบนี้อธิบายได้ไม่ดีนัก มันดูสับสนหากคุณไม่เข้าใจลักษณะรายการที่ไม่แน่นอน --- ในตัวอย่างนี้ ... เพื่อให้สิทธิ์อีกครั้งz, คุณมีการโทรแล้วmutable_z.z.pop(0) mutable_z.z.append(new_value)หากคุณเข้าใจผิดคุณจะมีองค์ประกอบมากกว่า 1 รายการและโปรแกรมของคุณจะทำงานโดยไม่คาดคิด
byxor

1
@byx หรือว่าคุณทำได้แค่: mutable_z.z[0] = newValue. มันเป็นการแฮ็กตามที่ระบุไว้
Srg

ใช่ฉันประหลาดใจที่พลาดวิธีที่ชัดเจนกว่าในการกำหนดใหม่
byxor

ฉันชอบมันแฮ็คจริง
WebOrCode
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.