Xác định loại của một đối tượng?


1791

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


44
Mặc dù nói chung tôi đồng ý với bạn, có những tình huống rất hữu ích để biết. Trong trường hợp cụ thể này, tôi đã thực hiện một số hack nhanh chóng mà cuối cùng tôi đã quay trở lại, vì vậy lần này bạn đã đúng. Nhưng trong một số trường hợp - khi sử dụng sự phản chiếu, chẳng hạn - điều quan trọng là phải biết loại đối tượng bạn đang xử lý.
Justin Ethier

67
@ S.Lott Tôi không đồng ý với điều đó; bằng cách có thể biết loại, bạn có thể đối phó với một số đầu vào biến thể đẹp và vẫn làm đúng. Nó cho phép bạn giải quyết các vấn đề giao diện vốn có dựa vào việc gõ vịt thuần túy (ví dụ: phương thức .bark () trên Cây có nghĩa là một cái gì đó hoàn toàn khác với trên Chó.) Ví dụ: bạn có thể tạo một chức năng thực hiện một số chức nă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ó giao diện khác nhau, nhưng kết quả cuối cùng là như nhau: thực hiện một số thao tác trên tệp đó.
Robert P

22
@ S.Lott Tôi hy vọng nó là một ví dụ rõ ràng; Tuy nhiên, đó là một điểm thất bại lớn của việc gõ vịt, và một điểm trykhông giúp được gì. Ví dụ: nếu bạn biết rằng người dùng có thể truyền vào một chuỗi hoặc một mảng, cả hai đều có khả năng 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 nhau. Đơn giản chỉ cần dựa vào một thử bắt trong những trường hợp đó sẽ thất bại theo những cách bất ngờ và kỳ lạ. Một giải pháp là tạo một phương thức riêng biệt, một phương pháp khác để thêm một kiểu kiểm tra nhỏ. Cá nhân tôi thích hành vi đa hình hơn nhiều phương thức thực hiện gần giống nhau ... nhưng đó chỉ là tôi :)
Robert P

22
@ S.Lott, còn kiểm tra đơn vị thì sao? Đôi khi bạn muốn kiểm tra của mình để xác minh rằng một hàm đang trả về một cái gì đó đúng loại. Một ví dụ rất thực tế là khi bạn có lớp nhà máy.
Elliot Cameron

17
Đối với một ví dụ ít gây tranh cãi, 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à biểu diễn nối tiếp. Trình tuần tự hóa 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 để xác định loại khử lưu lượng mà không yêu cầu thời gian chạy (hoặc ít nhất, bạn có thể cần nó để kiểm tra vệ sinh để bắt dữ liệu xấu trước khi nhập hệ thống của bạn!)
Karl

Câu trả lời:


1976

Có hai chức năng tích hợp giúp bạn xác định loại đối tượng. Bạn có thể sử dụng type() nếu bạn cần loại chính xác của một đối tượng và isinstance()để kiểm tra loại đối tượng đó với một thứ gì đó. Thông thường, bạn muốn sử dụng isistance()hầu hết thời gian vì nó rất mạnh mẽ và cũng hỗ trợ kiểu kế thừa.


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

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

Đ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ả về loại ngay lập tức của đối tượng, nhưng sẽ không thể cho bạn biết về kế thừa loại.

>>> type(b) is Test1
False

Để bao quát điều đó, bạn nên sử dụng isinstancechức năng. Điều này tất nhiên cũng hoạt động cho các loại tích hợp:

>>> 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 ưa thích để đảm bảo loại đối tượng bởi vì nó cũng sẽ chấp nhận các loại dẫn xuất. Vì vậy, trừ khi bạn thực sự cần loại đối tượng (vì bất kỳ lý do gì), sử dụng isinstance()được ưu tiên hơn type().

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

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

68
Tôi nghĩ rằng nó rõ ràng hơn để sử dụng isthay vì ==các loại là singletons
John La Rooy

18
@gnibbler, Trong trường hợp bạn sẽ đánh máy (mà bạn không nên làm để bắt đầu), isinstancedù sao đi nữa, đây là hình thức ưa thích, vì vậy không ==hoặc không iscần sử dụng.
Mike Graham

23
@Mike Graham, có những lúc typecâu trả lời là tốt nhất. Có những lúc isinstancecâ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 phù hợp hơn cho tình huống.
John La Rooy

6
@gnibbler, Điều đó có thể, mặc dù tôi chưa gặp phải tình huống type(foo) is SomeTypenào tốt hơn isinstance(foo, SomeType).
Mike Graham

5
@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 tranh luận của Sven không phải là PEP8, nhưng bạn cũng có thể sử dụng isinstancecho usecase của mình (kiểm tra một loạt các loại) và với cũng như xóa một cú pháp, có lợi thế lớn là bạn có thể nắm bắt các lớp con. ai đó sử dụng OrderedDictsẽ ghét mã của bạn thất bại bởi vì nó chỉ chấp nhận các ký tự thuần túy.
cừu bay

166

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'>

40

Nó có thể là nhiều Pythonic hơn để sử dụng một try... exceptkhối. Theo cách đó, nếu bạn có một lớp mà quẻ như một danh sách, hoặc các quẻ như một dict, nó sẽ hành xử đúng bất kể loại của nó thực sự là gì.

Để làm rõ, phương pháp "nói khác biệt" ưa thích giữa các loại biến là với một thứ gọi là gõ vịt : miễn là các phương thức (và 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 coi nó như những gì bạn mong đợi được. Ví dụ: nếu bạn có một lớp làm quá tải các toán tử khung getattrsetattr, nhưng sử dụng một số lược đồ bên trong vui nhộn, thì nó sẽ thích hợp để nó hoạt động như một từ điển nếu đó là những gì nó đang cố gắng mô phỏng.

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


16
Tuy nhiên, gõ vịt không thực sự nói về sự khác biệt. Đó là về việc sử dụng một giao diện chung.
Justin Ethier

5
Hãy cẩn thận - hầu hết các hướng dẫn về 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 dòng mã kiểm soát thông thường, thường là vì nó làm cho mã khó đọc. try... exceptlà 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

34

Trong 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'>

Coi chừng rằng trong lớp python 3.x và trong các lớp Kiểu mới (có thể tùy chọn từ lớp Python 2.6) và loại đã được hợp nhất và điều này đôi khi có thể dẫn đến kết quả không mong muốn. Chủ yếu là vì lý do này cách yêu thích của tôi về thử nghiệm loại / classes là đến isinstance xây dựng trong chức năng.


2
Quan điểm của bạn ở cuối là rất quan trọng. loại (obj) là Class không hoạt động chính xác, nhưng isinstance đã lừa. Tôi hiểu rằng isinstance được ưu tiên hơn, nhưng nó có lợi hơn là chỉ kiểm tra các loại dẫn xuất, như được đề xuất trong câu trả lời được chấp nhận.
mstbaum

__class__Hầu hết 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. Nhân tiện, tôi không hiểu mối quan tâm Python 3 của bạn - trên phiên bản như vậy, chỉ có mọi đối tượng đều có một __class__thuộc tính trỏ đến lớp thích hợp.
Alan Franzoni

21

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

Xác định loại đối tượng với type

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

Mặc dù nó hoạt động, tránh hai thuộc tính gạch dưới 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'>

kiểm tra loại

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

Đó 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ể 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 Thay thế Liskov, bạn muốn có thể sử dụng các thể hiện của lớp con mà không vi phạm mã của mình - và isinstancehỗ 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 kiếm một Lớp cơ sở trừu tượng cụ thể từ collectionshoặc 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 đừng kiểm tra loại rõ ràng

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

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 một lớp của một cá thể.
  • Sử dụng isinstanceđể kiểm tra 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 nó có ý nghĩa.

Luôn luôn có try/ exceptthay vì kiểm tra 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ẽ chuyển vào. Tôi không muốn làm lộn xộn một triển khai chính xác với xử lý ngoại lệ trừ khi tôi có một điều gì đó rất tốt để làm với ngoại lệ. Ngoại lệ nêu ra phải đủ để thông báo cho người dùng rằng họ cần sửa lỗi sử dụng.
Aaron Hall

13

Bạn có thể sử dụng type()hoặc isinstance().

>>> type([]) is list
True

Được cảnh báo rằng bạn có thể ghi đè listhoặc bất kỳ loại nào khác bằng cách chỉ định 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.

Để khắc phục điều 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

2
Tôi không chắc chắn cần phải chỉ ra rằng việc che giấu tên của loại dữ liệu dựng sẵn là không tốt cho trường hợp này. dictChuỗi của bạn 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 vấn đề đó là "Vậy thì đừng làm thế" . Đừng bỏ qua các tên loại dựng sẵn và mong muốn mọi thứ hoạt động chính xác.
Blckknght

3
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 đó đừ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 nghĩ rằng đây là một cơ hội có liên quan để làm điều đó. Tôi có nghĩa là cho phương pháp thận trọng để trông xấu xí và minh họa tại sao họ có thể không muốn làm điều đó, để họ quyết định.
deed02392

type () không hoạt động như mong đợi trên Python 2.x cho các trường hợp cổ điển.
Alan Franzoni

5

Mặc dù các câu hỏi khá cũ, tôi đã tình cờ phát hiện ra điều này trong khi tự mình tìm ra một cách thích hợp và tôi nghĩ nó vẫn cần làm rõ, ít nhất là đối với Python 2.x (không kiểm tra Python 3, nhưng vì vấn đề phát sinh với các lớp cổ điển phiên bản như vậy, có lẽ không thành vấn đề).

Ở đâ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 đố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 đều ổn trong nhiều bình luậ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 thể hiệ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

Thực hiện đoạn trích này sẽ mang lại:

Are o and t instances of the same class? True

Trong đó, tôi lập luận, không phải là điều mà hầu hết mọi người mong đợi.

Cách __class__tiếp cận gần nhất với tính chính xác, nhưng nó sẽ không hoạt động trong một trường hợp quan trọng: khi đối tượng truyền vào là một lớp kiểu cũ (không phải là một thể hiện!), Vì các đố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ĩ rằng đáp ứng 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

5

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

3

Ngoài các câu trả lời trước đó, cần đề cập đến sự tồn tại của collections.abcnó chứa 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 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ỉ muốn xem liệu đối tượng bạn có cho phép nhận vật phẩm hay không, hãy sử dụng collections.abc.Sequence:

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

nếu bạn thực sự quan tâm đến các đối tượng cho phép nhận, cài đặt xóa các mục (nghĩa là các chuỗi có thể thay đổi ), bạn sẽ chọn collections.abc.MutableSequence.

Nhiều ABCs khác được định nghĩa ở đó, Mappingcho các đối tượng có thể được sử dụng như bản đồ, Iterable, Callable, vân vân. 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.


1

Nói chung, bạn có thể trích xuất một chuỗi từ đối tượng với tên lớp,

str_class = object.__class__.__name__

và sử dụng nó để so sánh,

if str_class == 'dict':
    # blablabla..
elif str_class == 'customclass':
    # blebleble..

1

Trong nhiều trường hợp thực tế thay vì sử dụng typehoặc isinstancebạn cũng có thể sử dụng @functools.singledispatch, được sử dụng để xác định các hàm chung ( hàm bao gồm nhiều hàm thực hiện cùng một hoạt động cho các loại khác nhau ).

Nói cách khác, bạn sẽ muốn sử dụng nó khi bạn có một mã như sau:

def do_something(arg):
    if isinstance(arg, int):
        ... # some code specific to processing integers
    if isinstance(arg, str):
        ... # some code specific to processing strings
    if isinstance(arg, list):
        ... # some code specific to processing lists
    ...  # etc

Đây là một ví dụ nhỏ về cách thức hoạt động của nó:

from functools import singledispatch


@singledispatch
def say_type(arg):
    raise NotImplementedError(f"I don't work with {type(arg)}")


@say_type.register
def _(arg: int):
    print(f"{arg} is an integer")


@say_type.register
def _(arg: bool):
    print(f"{arg} is a boolean")
>>> say_type(0)
0 is an integer
>>> say_type(False)
False is a boolean
>>> say_type(dict())
# long error traceback ending with:
NotImplementedError: I don't work with <class 'dict'>

Ngoài ra, chúng ta có thể sử dụng các lớp trừu tượng để bao gồm nhiều loại cùng một lúc:

from collections.abc import Sequence


@say_type.register
def _(arg: Sequence):
    print(f"{arg} is a sequence!")
>>> say_type([0, 1, 2])
[0, 1, 2] is a sequence!
>>> say_type((1, 2, 3))
(1, 2, 3) is a sequence!

0

type()là một giải pháp tốt hơn isinstance(), đặc biệt là booleans:

TrueFalsechỉ là những từ khóa có nghĩa 10trong python. Như vậy

isinstance(True, int)

isinstance(False, int)

Cả hai trở về True. Cả hai booleans là một thể hiện của một số nguyên. type()tuy nhiên, thông minh hơn:

type(True) == int

trả lại False.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.