Câu hỏi Xác định loại đối tượng?


Có cách nào đơn giản để xác định xem biến có phải là danh sách, từ điển hay thứ gì khác không? Tôi nhận được một đối tượng có thể là một trong hai loại và tôi cần để có thể cho biết sự khác biệt.


1384
2018-02-08 21:37


gốc


Trong khi nói chung tôi đồng ý với bạn, có những tình huống mà nó là hữu ích để biết. Trong trường hợp đặc biệt này, tôi đã thực hiện một số tấn công nhanh chóng mà cuối cùng tôi đã quay trở lại, vì vậy bạn chính xác lần này. Nhưng trong một số trường hợp - khi sử dụng sự phản chiếu, ví dụ - điều quan trọng là phải biết loại đối tượng bạn đang xử lý. - Justin Ethier
@ S.Lott tôi không đồng ý với điều đó; bằng cách có thể biết được loại, bạn có thể xử lý một số đầu vào biến thể khá và vẫn làm điều đúng. Nó cho phép bạn làm việc xung quanh các vấn đề giao diện vốn có với việc dựa vào kiểu gõ vịt thuần túy (ví dụ, phương thức .bark () trên cây có nghĩa là hoàn toàn khác với trên một con chó.) Ví dụ, bạn có thể tạo một hàm hoạt động một tệp chấp nhận một chuỗi (ví dụ: đường dẫn), đối tượng đường dẫn hoặc danh sách. Tất cả đều có các giao diện khác nhau, nhưng kết quả cuối cùng là giống nhau: thực hiện một số thao tác trên tệp đó. - Robert P
@ S.Lott Tôi hy vọng nó sẽ được rõ ràng rằng đó là một ví dụ contrived; Tuy nhiên đó là một điểm thất bại chính của việc gõ vịt, và một try không giúp gì được. Ví dụ, nếu bạn biết rằng một người dùng có thể truyền vào một chuỗi hoặc một mảng, cả hai đều có thể lập chỉ mục, nhưng chỉ mục đó có nghĩa là một cái gì đó hoàn toàn khác. Đơn giản chỉ cần dựa vào một thử-catch trong những trường hợp sẽ thất bại trong những cách bất ngờ và kỳ lạ. Một giải pháp là tạo ra một phương pháp riêng biệt, một phương pháp khác để thêm một loại kiểm tra nhỏ. Cá nhân tôi thích hành vi đa hình hơn nhiều phương pháp làm gần như cùng một điều ... nhưng đó chỉ là tôi :) - Robert P
@ S.Lott, những gì về thử nghiệm đơn vị? Đôi khi bạn muốn các bài kiểm tra của mình xác minh rằng một hàm trả về một kiểu đúng nào đó. Một ví dụ rất thực tế là khi bạn có nhà máy lớp học. - Elliot Cameron
Đối với một ví dụ ít contrived, hãy xem xét một serializer / deserializer. Theo định nghĩa, bạn đang chuyển đổi giữa các đối tượng do người dùng cung cấp và một biểu diễn được tuần tự hóa. Trình tuần tự cần xác định loại đối tượng bạn truyền vào và bạn có thể không có thông tin đầy đủ để xác định loại không được yêu cầu mà không hỏi thời gian chạy (hoặc ít nhất, bạn có thể cần nó để kiểm tra sanity để thu thập dữ liệu xấu trước khi nhập hệ thống của bạn!) - Karl


Các câu trả lời:


Để có được loại đối tượng, bạn có thể sử dụng tính năng tích hợp sẵn type() chức năng. Truyền một đối tượng làm tham số duy nhất sẽ trả về đối tượng kiểu của đối tượng đó:

>>> type([]) is list
True
>>> type({}) is dict
True
>>> type('') is str
True
>>> type(0) is int
True
>>> type({})
<type 'dict'>
>>> type([])
<type 'list'>

Điều này tất nhiên cũng hoạt động cho các loại tùy chỉnh:

>>> class Test1 (object):
        pass
>>> class Test2 (Test1):
        pass
>>> a = Test1()
>>> b = Test2()
>>> type(a) is Test1
True
>>> type(b) is Test2
True

Lưu ý rằng type() sẽ chỉ trả lại loại đối tượng ngay lập tức, nhưng sẽ không thể cho bạn biết về loại thừa kế.

>>> type(b) is Test1
False

Để trang trải điều đó, bạn nên sử dụng isinstance chức năng. Điều này tất nhiên cũng hoạt động đối với các kiểu tích hợp sẵn:

>>> isinstance(b, Test1)
True
>>> isinstance(b, Test2)
True
>>> isinstance(a, Test1)
True
>>> isinstance(a, Test2)
False
>>> isinstance([], list)
True
>>> isinstance({}, dict)
True

isinstance() thường là cách ưu tiên để đảm bảo loại đối tượng vì nó cũng sẽ chấp nhận các kiểu có nguồn gốc. Vì vậy, trừ khi bạn thực sự cần đối tượng kiểu (vì bất kỳ lý do gì), sử dụng isinstance() được ưa thích hơn type().

Tham số thứ hai của isinstance() cũng chấp nhận một nhóm các loại, do đó, có thể kiểm tra nhiều loại cùng một lúc. isinstance sau đó sẽ trả về true, nếu đối tượng là bất kỳ loại nào trong số đó:

>>> isinstance([], (tuple, list, set))
True

1608
2018-02-08 21:40



Tôi nghĩ rằng nó rõ ràng hơn để sử dụng is thay vì == như các loại là singletons - John La Rooy
@gnibbler, Trong trường hợp bạn sẽ đánh máy (bạn không nên làm gì để bắt đầu), isinstance là hình thức ưa thích dù sao đi nữa, vì vậy không phải == hoặc là is cần được sử dụng. - Mike Graham
@Mike Graham, có những lúc type là câu trả lời hay nhất. Có những lúc isinstance là câu trả lời hay nhất và có những lúc vịt gõ là câu trả lời hay nhất. Điều quan trọng là phải biết tất cả các tùy chọn để bạn có thể chọn tùy chọn nào phù hợp hơn cho tình huống. - John La Rooy
@gnibbler, Đó có thể là, mặc dù tôi chưa gặp phải tình huống type(foo) is SomeType sẽ tốt hơn isinstance(foo, SomeType). - Mike Graham
@poke: tôi hoàn toàn đồng ý về PEP8, nhưng bạn đang tấn công một người rơm ở đây: phần quan trọng trong lập luận của Sven không phải là PEP8, nhưng bạn có thể sử dụng isinstancecho usecase của bạn (kiểm tra cho một loạt các loại) là tốt, và với một cú pháp sạch sẽ là tốt, trong đó có lợi thế lớn mà bạn có thể nắm bắt các lớp con. ai đó đang sử dụng OrderedDict sẽ ghét mã của bạn để thất bại vì nó chỉ chấp nhận dicts thuần túy. - flying sheep


Bạn có thể làm điều đó bằng cách sử dụng type():

>>> a = []
>>> type(a)
<type 'list'>
>>> f = ()
>>> type(f)
<type 'tuple'>

142
2018-02-08 21:39





Nó có thể được nhiều Pythonic để sử dụng một try...except khối. Bằng cách đó, nếu bạn có một lớp học mà quacks như một danh sách, hoặc quacks như một dict, nó sẽ cư xử đúng cách bất kể loại của nó có thật không Là.

Để làm rõ, phương pháp ưa thích "nói sự khác biệt" giữa các loại biến là với một cái gì đó gọi là vịt gõ: miễn là các phương thức (và các kiểu trả về) mà một biến đáp ứng là những gì chương trình con của bạn mong đợi, hãy xử lý nó như những gì bạn mong đợi. Ví dụ, nếu bạn có một lớp mà quá tải các toán tử khung với getattr và setattr, nhưng sử dụng một số chương trình nội bộ vui, nó sẽ là thích hợp cho nó để hành xử như một từ điển nếu đó là những gì nó đang cố gắng để thi đua.

Vấn đề khác với type(A) is type(B) kiểm tra là nếu A là một phân lớp của B, nó đánh giá false khi lập trình, bạn sẽ hy vọng nó sẽ true. Nếu một đối tượng là một phân lớp của một danh sách, nó sẽ hoạt động như một danh sách: kiểm tra kiểu như được trình bày trong câu trả lời khác sẽ ngăn chặn điều này. (isinstance sẽ hoạt động, tuy nhiên).


37
2018-02-08 21:43



Tuy nhiên, việc gõ vịt không thực sự nói về sự khác biệt. Đó là về cách sử dụng một giao diện chung. - Justin Ethier
Hãy cẩn thận - hầu hết các hướng dẫn kiểu mã hóa khuyên bạn không nên sử dụng xử lý ngoại lệ như là một phần của luồng điều khiển thông thường của mã, thường là vì nó làm cho mã khó đọc. try... except là một giải pháp tốt khi bạn muốn xử lý lỗi, nhưng không phải khi quyết định hành vi dựa trên loại. - Rens van der Heijden


Trên các trường hợp của đối tượng, bạn cũng có:

__class__

thuộc tính. Đây là một mẫu được lấy từ bảng điều khiển Python 3.3

>>> str = "str"
>>> str.__class__
<class 'str'>
>>> i = 2
>>> i.__class__
<class 'int'>
>>> class Test():
...     pass
...
>>> a = Test()
>>> a.__class__
<class '__main__.Test'>

Cẩn thận rằng trong python 3.x và trong lớp New-Style (aviable tùy chọn từ Python 2.6) lớp và loại đã được sáp nhập và điều này đôi khi có thể dẫn đến kết quả bất ngờ. Chủ yếu vì lý do này, cách yêu thích của tôi về các loại / lớp kiểm tra là isinstance được xây dựng trong chức năng.


31
2018-06-26 21:38



Điểm của bạn ở cuối là rất quan trọng. type (obj) là Class không hoạt động chính xác, nhưng isinstance thực hiện thủ thuật này. Tôi hiểu rằng isinstance được ưa thích anyway, nhưng nó có lợi hơn so với chỉ kiểm tra các loại có nguồn gốc, như đề xuất trong câu trả lời được chấp nhận. - mstbaum
__class__ chủ yếu là OK trên Python 2.x, các đối tượng duy nhất trong Python không có __class__ thuộc tính là các lớp kiểu cũ AFAIK. Tôi không hiểu mối quan tâm Python 3 của bạn, bằng cách này - trên phiên bản như vậy, mọi đối tượng đều có __class__ thuộc tính trỏ đến lớp thích hợp. - Alan Franzoni


Xác định loại đối tượng Python

Xác định loại đối tượng bằng type

>>> obj = object()
>>> type(obj)
<class 'object'>

Mặc dù nó hoạt động, tránh các thuộc tính gạch dưới kép như __class__ - chúng không công khai về mặt ngữ nghĩa, và, trong khi có lẽ không phải trong trường hợp này, các hàm dựng sẵn thường có hành vi tốt hơn.

>>> obj.__class__ # avoid this!
<class 'object'>

loại kiểm tra

Có cách nào đơn giản để xác định xem biến có phải là danh sách, từ điển hay thứ gì khác không? Tôi nhận được một đối tượng có thể là một trong hai loại và tôi cần để có thể cho biết sự khác biệt.

Vâng, đó là một câu hỏi khác, không sử dụng loại - sử dụng isinstance:

def foo(obj):
    """given a string with items separated by spaces, 
    or a list or tuple, 
    do something sensible
    """
    if isinstance(obj, str):
        obj = str.split()
    return _foo_handles_only_lists_or_tuples(obj)

Điều này bao gồm trường hợp người dùng của bạn có thể đang làm điều gì đó thông minh hoặc hợp lý bằng cách phân lớp str - theo nguyên tắc của Thay thế Liskov, bạn muốn có thể sử dụng các cá thể lớp con mà không vi phạm mã của bạn - và isinstance hỗ trợ điều này.

Sử dụng trừu tượng

Thậm chí tốt hơn, bạn có thể tìm một lớp cơ sở trừu tượng cụ thể từ collections hoặc là numbers:

from collections import Iterable
from numbers import Number

def bar(obj):
    """does something sensible with an iterable of numbers, 
    or just one number
    """
    if isinstance(obj, Number): # make it a 1-tuple
        obj = (obj,)
    if not isinstance(obj, Iterable):
        raise TypeError('obj must be either a number or iterable of numbers')
    return _bar_sensible_with_iterable(obj)

Hoặc chỉ cần không rõ ràng loại- kiểm tra

Hoặc, có lẽ tốt nhất của tất cả, sử dụng gõ vịt, và không rõ ràng loại kiểm tra mã của bạn. Duck-gõ hỗ trợ Liskov thay thế với sự sang trọng hơn và ít verbosity.

def baz(obj):
    """given an obj, a dict (or anything with an .items method) 
    do something sensible with each key-value pair
    """
    for key, value in obj.items():
        _baz_something_sensible(key, value)

Phần kết luận

  • Sử dụng type để thực sự có được lớp của một cá thể.
  • Sử dụng isinstance để kiểm tra một cách rõ ràng các lớp con thực tế hoặc trừu tượng đã đăng ký.
  • Và chỉ cần tránh kiểm tra loại ở nơi có ý nghĩa.

14
2018-03-14 21:12



Luôn luôn có try/except thay vì kiểm tra một cách rõ ràng. - toonarmycaptain
Có lẽ đó là những gì người dùng sẽ làm nếu họ không chắc chắn về các loại họ sẽ được đi qua. Tôi không muốn lộn xộn một thực hiện chính xác với xử lý ngoại lệ trừ khi tôi có một cái gì đó rất tốt để làm với ngoại lệ. Ngoại lệ được nêu ra là đủ để thông báo cho người dùng rằng họ cần phải sửa chữa việc sử dụng của họ. - Aaron Hall♦


Bạn có thể dùng type() hoặc là isinstance().

>>> type([]) is list
True

Được cảnh báo rằng bạn có thể list hoặc bất kỳ loại nào khác bằng cách gán một biến trong phạm vi hiện tại cùng tên.

>>> the_d = {}
>>> t = lambda x: "aight" if type(x) is dict else "NOPE"
>>> t(the_d) 'aight'
>>> dict = "dude."
>>> t(the_d) 'NOPE'

Ở trên chúng ta thấy rằng dict được gán lại cho một chuỗi, do đó kiểm tra:

type({}) is dict

... thất bại.

Để giải quyết vấn đề này và sử dụng type() thận trọng hơn:

>>> import __builtin__
>>> the_d = {}
>>> type({}) is dict
True
>>> dict =""
>>> type({}) is dict
False
>>> type({}) is __builtin__.dict
True

10
2018-05-31 21:57



Tôi không chắc chắn nó cần thiết để chỉ ra rằng shadowing tên của một kiểu dữ liệu nội tuyến là xấu cho trường hợp này. Của bạn dict chuỗi cũng sẽ thất bại đối với nhiều mã khác, như dict([("key1", "value1"), ("key2", "value2")]). Câu trả lời cho những loại vấn đề đó là "Vậy đừng làm thế". Không đổ bóng tên kiểu dựng sẵn và mong đợi mọi thứ hoạt động bình thường. - Blckknght
Tôi đồng ý với bạn về phần "không làm điều đó". Nhưng thực sự để nói với ai đó không làm điều gì đó ít nhất bạn nên giải thích tại sao không và tôi nhận ra đây là một cơ hội có liên quan để làm điều đó. Tôi có nghĩa là cho các phương pháp thận trọng để nhìn xấu xí và minh họa tại sao họ có thể không muốn làm điều đó, để lại cho họ để quyết định. - deed02392
type () không hoạt động như mong đợi trên Python 2.x cho các phiên bản cổ điển. - Alan Franzoni


Trong khi các câu hỏi là khá cũ, tôi tình cờ gặp điều này trong khi tìm ra một cách thích hợp bản thân mình, và tôi nghĩ rằng nó vẫn cần làm rõ, ít nhất cho Python 2.x (đã không kiểm tra trên Python 3, nhưng kể từ khi vấn đề phát sinh với các lớp học cổ điển mà đã đi trên phiên bản như vậy, nó có thể không quan trọng).

Ở đây tôi đang cố gắng trả lời câu hỏi của tiêu đề: làm thế nào tôi có thể xác định loại của một đối tượng tùy ý? Các đề xuất khác về việc sử dụng hoặc không sử dụng isinstance là tốt trong nhiều ý kiến ​​và câu trả lời, nhưng tôi không giải quyết những lo ngại đó.

Vấn đề chính với type() cách tiếp cận là nó không hoạt động đúng với các phiên bản kiểu cũ:

class One:
    pass

class Two:
    pass


o = One()
t = Two()

o_type = type(o)
t_type = type(t)

print "Are o and t instances of the same class?", o_type is t_type

Việc thực thi đoạn mã này sẽ mang lại:

Are o and t instances of the same class? True

Mà, tôi cho rằng, không phải là điều mà hầu hết mọi người mong đợi.

Các __class__ cách tiếp cận gần nhất là đúng đắn, nhưng nó sẽ không hoạt động trong một trường hợp quan trọng: khi đối tượng được truyền vào là một kiểu cũ lớp học (không phải là một ví dụ!), vì những đối tượng đó thiếu thuộc tính như vậy.

Đây là đoạn mã nhỏ nhất mà tôi có thể nghĩ đến, đáp ứng các câu hỏi hợp pháp như vậy theo một cách nhất quán:

#!/usr/bin/env python
from types import ClassType
#we adopt the null object pattern in the (unlikely) case
#that __class__ is None for some strange reason
_NO_CLASS=object()
def get_object_type(obj):
    obj_type = getattr(obj, "__class__", _NO_CLASS)
    if obj_type is not _NO_CLASS:
        return obj_type
    # AFAIK the only situation where this happens is an old-style class
    obj_type = type(obj)
    if obj_type is not ClassType:
        raise ValueError("Could not determine object '{}' type.".format(obj_type))
    return obj_type

4
2018-06-17 08:35





Như một câu trả lời cho các câu trả lời trước, nó đáng nói đến sự tồn tại của collections.abc trong đó có một số lớp cơ sở trừu tượng (ABC) bổ sung cho việc gõ vịt.

Ví dụ, thay vì kiểm tra một cách rõ ràng nếu một cái gì đó là một danh sách với:

isinstance(my_obj, list)

bạn có thể, nếu bạn chỉ quan tâm đến việc xem liệu đối tượng bạn có cho phép nhận các vật dụng hay không, hãy sử dụng collections.abc.Sequence:

from collections.abc import Sequence
isinstance(my_obj, Sequence) 

nếu bạn nghiêm túc quan tâm đến các vật thể cho phép nhận, thiết lập  xóa mục (ví dụ: có thể thay đổi trình tự), bạn sẽ chọn tham gia collections.abc.MutableSequence.

Nhiều ABC khác được định nghĩa ở đó, Mapping đối với các đối tượng có thể được sử dụng làm bản đồ, Iterable, Callable, v.v. Một danh sách đầy đủ của tất cả những điều này có thể được nhìn thấy trong tài liệu cho collections.abc.


3
2017-12-07 15:50





cẩn thận khi sử dụng isinstance

isinstance(True, bool)
True
>>> isinstance(True, int)
True

nhưng loại

type(True) == bool
True
>>> type(True) == int
False

2
2017-11-14 11:22