Tuples được đặt tên là gì?
Một tuple được đặt tên là một tuple.
Nó làm mọi thứ một tuple có thể.
Nhưng nó không chỉ là một tuple.
Đây là một lớp con cụ thể của một bộ dữ liệu được tạo lập trình theo đặc điểm kỹ thuật của bạn, với các trường được đặt tên và độ dài cố định.
Điều này, ví dụ, tạo ra một lớp con của bộ dữ liệu và ngoài việc có độ dài cố định (trong trường hợp này là ba), nó có thể được sử dụng ở mọi nơi mà một bộ dữ liệu được sử dụng mà không bị phá vỡ. Điều này được gọi là thay thế Liskov.
Mới trong Python 3.6 , chúng ta có thể sử dụng một định nghĩa lớptyping.NamedTuple
để tạo ra một tên được đặt tên:
from typing import NamedTuple
class ANamedTuple(NamedTuple):
"""a docstring"""
foo: int
bar: str
baz: list
Ở trên giống như bên dưới, ngoại trừ bên trên có chú thích loại và một chuỗi doc. Dưới đây có sẵn trong Python 2+:
>>> from collections import namedtuple
>>> class_name = 'ANamedTuple'
>>> fields = 'foo bar baz'
>>> ANamedTuple = namedtuple(class_name, fields)
Điều này bắt đầu nó:
>>> ant = ANamedTuple(1, 'bar', [])
Chúng ta có thể kiểm tra nó và sử dụng các thuộc tính của nó:
>>> ant
ANamedTuple(foo=1, bar='bar', baz=[])
>>> ant.foo
1
>>> ant.bar
'bar'
>>> ant.baz.append('anything')
>>> ant.baz
['anything']
Giải thích sâu hơn
Để hiểu các bộ dữ liệu được đặt tên, trước tiên bạn cần biết một bộ dữ liệu là gì. Một tuple về cơ bản là một danh sách bất biến (không thể thay đổi tại chỗ trong bộ nhớ).
Đây là cách bạn có thể sử dụng một tuple thông thường:
>>> student_tuple = 'Lisa', 'Simpson', 'A'
>>> student_tuple
('Lisa', 'Simpson', 'A')
>>> student_tuple[0]
'Lisa'
>>> student_tuple[1]
'Simpson'
>>> student_tuple[2]
'A'
Bạn có thể mở rộng một tuple với giải nén lặp lại:
>>> first, last, grade = student_tuple
>>> first
'Lisa'
>>> last
'Simpson'
>>> grade
'A'
Các bộ dữ liệu được đặt tên là bộ dữ liệu cho phép các phần tử của chúng được truy cập theo tên thay vì chỉ mục lục!
Bạn tạo một cái tên như thế này:
>>> from collections import namedtuple
>>> Student = namedtuple('Student', ['first', 'last', 'grade'])
Bạn cũng có thể sử dụng một chuỗi với các tên được phân tách bằng dấu cách, cách sử dụng API dễ đọc hơn một chút:
>>> Student = namedtuple('Student', 'first last grade')
Làm thế nào để sử dụng chúng?
Bạn có thể làm mọi thứ mà bộ dữ liệu có thể làm (xem bên trên) cũng như làm như sau:
>>> named_student_tuple = Student('Lisa', 'Simpson', 'A')
>>> named_student_tuple.first
'Lisa'
>>> named_student_tuple.last
'Simpson'
>>> named_student_tuple.grade
'A'
>>> named_student_tuple._asdict()
OrderedDict([('first', 'Lisa'), ('last', 'Simpson'), ('grade', 'A')])
>>> vars(named_student_tuple)
OrderedDict([('first', 'Lisa'), ('last', 'Simpson'), ('grade', 'A')])
>>> new_named_student_tuple = named_student_tuple._replace(first='Bart', grade='C')
>>> new_named_student_tuple
Student(first='Bart', last='Simpson', grade='C')
Một bình luận hỏi:
Trong một kịch bản hoặc chương trình lớn, người ta thường định nghĩa một bộ dữ liệu có tên ở đâu?
Các loại bạn tạo với namedtuple
về cơ bản là các lớp bạn có thể tạo với tốc ký dễ dàng. Đối xử với họ như các lớp học. Xác định chúng ở cấp độ mô-đun, để dưa chua và những người dùng khác có thể tìm thấy chúng.
Ví dụ hoạt động, ở cấp độ mô-đun toàn cầu:
>>> from collections import namedtuple
>>> NT = namedtuple('NT', 'foo bar')
>>> nt = NT('foo', 'bar')
>>> import pickle
>>> pickle.loads(pickle.dumps(nt))
NT(foo='foo', bar='bar')
Và điều này chứng tỏ sự thất bại trong việc tra cứu định nghĩa:
>>> def foo():
... LocalNT = namedtuple('LocalNT', 'foo bar')
... return LocalNT('foo', 'bar')
...
>>> pickle.loads(pickle.dumps(foo()))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
_pickle.PicklingError: Can't pickle <class '__main__.LocalNT'>: attribute lookup LocalNT on __main__ failed
Tại sao / khi nào tôi nên sử dụng bộ dữ liệu có tên thay vì bộ dữ liệu thông thường?
Sử dụng chúng khi nó cải thiện mã của bạn để có ngữ nghĩa của các phần tử tuple được thể hiện trong mã của bạn.
Bạn có thể sử dụng chúng thay vì một đối tượng nếu bạn thường sử dụng một đối tượng có thuộc tính dữ liệu không thay đổi và không có chức năng.
Bạn cũng có thể phân lớp chúng để thêm chức năng, ví dụ :
class Point(namedtuple('Point', 'x y')):
"""adding functionality to a named tuple"""
__slots__ = ()
@property
def hypot(self):
return (self.x ** 2 + self.y ** 2) ** 0.5
def __str__(self):
return 'Point: x=%6.3f y=%6.3f hypot=%6.3f' % (self.x, self.y, self.hypot)
Tại sao / khi nào tôi nên sử dụng bộ dữ liệu bình thường thay vì bộ dữ liệu được đặt tên?
Nó có thể là một hồi quy để chuyển từ sử dụng bộ dữ liệu được đặt tên sang bộ dữ liệu. Quyết định thiết kế trả trước xoay quanh việc liệu chi phí từ mã bổ sung có liên quan có xứng đáng với khả năng đọc được cải thiện khi sử dụng bộ dữ liệu hay không.
Không có bộ nhớ thêm được sử dụng bởi các bộ dữ liệu được đặt tên so với bộ dữ liệu.
Có bất kỳ loại "danh sách được đặt tên" (một phiên bản có thể thay đổi của bộ dữ liệu được đặt tên) không?
Bạn đang tìm kiếm một đối tượng có rãnh thực hiện tất cả các chức năng của danh sách có kích thước tĩnh hoặc danh sách được phân lớp hoạt động giống như một tuple có tên (và bằng cách nào đó chặn danh sách thay đổi kích thước.)
Một ví dụ bây giờ được mở rộng và thậm chí có thể thay thế Liskov, ví dụ đầu tiên:
from collections import Sequence
class MutableTuple(Sequence):
"""Abstract Base Class for objects that work like mutable
namedtuples. Subclass and define your named fields with
__slots__ and away you go.
"""
__slots__ = ()
def __init__(self, *args):
for slot, arg in zip(self.__slots__, args):
setattr(self, slot, arg)
def __repr__(self):
return type(self).__name__ + repr(tuple(self))
# more direct __iter__ than Sequence's
def __iter__(self):
for name in self.__slots__:
yield getattr(self, name)
# Sequence requires __getitem__ & __len__:
def __getitem__(self, index):
return getattr(self, self.__slots__[index])
def __len__(self):
return len(self.__slots__)
Và để sử dụng, chỉ cần phân lớp và xác định __slots__
:
class Student(MutableTuple):
__slots__ = 'first', 'last', 'grade' # customize
>>> student = Student('Lisa', 'Simpson', 'A')
>>> student
Student('Lisa', 'Simpson', 'A')
>>> first, last, grade = student
>>> first
'Lisa'
>>> last
'Simpson'
>>> grade
'A'
>>> student[0]
'Lisa'
>>> student[2]
'A'
>>> len(student)
3
>>> 'Lisa' in student
True
>>> 'Bart' in student
False
>>> student.first = 'Bart'
>>> for i in student: print(i)
...
Bart
Simpson
A