Cách chính tắc để kiểm tra loại trong Python là gì?


1278

Cách tốt nhất để kiểm tra xem một đối tượng nhất định có thuộc loại nhất định không? Làm thế nào về việc kiểm tra xem đối tượng kế thừa từ một loại nhất định?

Hãy nói rằng tôi có một đối tượng o. Làm thế nào để tôi kiểm tra xem nó là một str?


7
Chà, cách tiếp cận kinh điển trong Python là không kiểm tra kiểu nào cả (trừ khi bạn gỡ lỗi). Thông thường bạn chỉ cần cố gắng sử dụng nó như một chuỗi (ví dụ: nối với các chuỗi khác, in ra bàn điều khiển, v.v.); nếu bạn nghĩ rằng nó có thể thất bại, hãy sử dụng thử / ngoại trừ hoặc hasattr. Điều đó nói rằng, câu trả lời được chấp nhận là cách kinh điển để làm những gì bạn thường "không nên làm" trong thế giới Python. Mọi chi tiết, google "Python gõ vịt" hay đọc những: voidspace.org.uk/python/articles/duck_typing.shtml stackoverflow.com/questions/610883/...
Jon Coombs

9
Tôi nghĩ rằng ông Coombs đang xem xét các ví dụ như các lớp tuần tự hóa không phải JSON. Nếu đặt một khối dữ liệu lớn thông qua một hàm (có mã mà người ta không thể tác động), người ta có thể muốn chuyển đổi một số phần nhất định của dữ liệu đó thành <str> trước khi truyền nó. Ít nhất đó là cách tôi kết thúc trên trang này ...
John Carrell

2
Có vẻ như lý do phổ biến nhất để yêu cầu điều này là người ta muốn phân biệt giữa chuỗi và lặp của chuỗi. Đây là một câu hỏi khó vì các chuỗi các chuỗi lặp - một chuỗi ký tự thậm chí là một chuỗi của chính nó (lần trước tôi đã kiểm tra - có lẽ người ta không nên dựa vào nó). Nhưng bất cứ ai cũng đã từng sử dụng cho một cái gì đó giống như chuỗi? . Vì vậy, câu trả lời cho "Tôi nên làm gì để phân biệt giữa các chuỗi và các lần lặp khác của chuỗi?" là đúng: "Nó phụ thuộc vào những gì bạn đang cố gắng làm". :-D
clacke

2
Chú thích kiểu Python bây giờ là một điều. Hãy nhìn vào mypy
Sheena

Câu trả lời:


1522

Để kiểm tra xem olà một thể hiện của strhoặc bất kỳ lớp con nào str, hãy sử dụng isinstance (đây sẽ là cách "chính tắc"):

if isinstance(o, str):

Để kiểm tra xem loại của ocó chính xác strkhông (loại trừ các lớp con):

if type(o) is str:

Những điều sau đây cũng hoạt động và có thể hữu ích trong một số trường hợp:

if issubclass(type(o), str):

Xem Hàm dựng sẵn trong Tài liệu tham khảo thư viện Python để biết thông tin liên quan.

Thêm một lưu ý: trong trường hợp này, nếu bạn đang sử dụng Python 2, bạn thực sự có thể muốn sử dụng:

if isinstance(o, basestring):

bởi vì điều này cũng sẽ bắt được các chuỗi Unicode ( unicodekhông phải là một lớp con của strcả hai strunicodelà các lớp con của basestring). Lưu ý rằng basestringkhông còn tồn tại trong Python 3, nơi có sự phân tách nghiêm ngặt của chuỗi ( str) và dữ liệu nhị phân ( bytes).

Ngoài ra, isinstancechấp nhận một bộ các lớp. Điều này sẽ trả về Truenếu olà một thể hiện của bất kỳ lớp con nào trong số (str, unicode):

if isinstance(o, (str, unicode)):

31
str .__ các lớp con __ () chỉ trả về các lớp con trực tiếp của str và không làm điều tương tự như ngay bây giờ lớp () hoặc isinstance (). (Để làm điều đó, bạn sẽ phải gọi đệ quy .__ lớp con __ ().
Thomas Wouters

15
Đây là một câu trả lời tốt, nhưng tôi nghĩ rằng nó thực sự nên bắt đầu với một cảnh báo rằng bạn thường không nên làm điều này trong Python. Như vậy, dường như nó xác nhận giả định rằng đây là "điều kinh điển cần làm trong Python", điều này không phải.
Jon Coombs

4
Đây là những câu trả lời python2. Ví dụ, không có basestring trong python3.
dfrankow

4
Sự khác biệt giữa cá thể và "chính xác" là gì? Nếu vậy type(a) is Objectthì cũng không đúng isinstance(a, Object). Tuy nhiên, nếu type(a) is SubClassOfObject, sau đó type(a) is Object == False, nhưng isinstance(a, Object) == True. Đúng?
mavavilj

1
@mavavilj - a is bcó nghĩa là a và b là cùng một thứ, nghĩa là các tham chiếu đến cùng một thực thể trong bộ nhớ. Vì vậy, absẽ phải là cùng một lớp, không phải là các lớp con, như với isinstance(). Xem ví dụ stackoverflow.com/a/133024/1072212
Terry Brown

196

Các nhất cách Pythonic để kiểm tra kiểu của một đối tượng là ... không kiểm tra nó.

Vì Python khuyến khích Gõ vịt , bạn chỉ nên try...exceptsử dụng các phương thức của đối tượng theo cách bạn muốn sử dụng chúng. Vì vậy, nếu chức năng của bạn đang tìm kiếm một đối tượng tệp có thể ghi, đừng kiểm tra xem đó có phải là một lớp con không file, chỉ cần thử sử dụng nó.write() phương thức !

Tất nhiên, đôi khi những trừu tượng tốt đẹp này bị phá vỡ và isinstance(obj, cls)là những gì bạn cần. Nhưng sử dụng một cách tiết kiệm.


75
IMHO, cách Pythonic nhất là đối phó với bất kỳ đối số nào được đưa ra. Trong mã của tôi, tôi thường không thể biết nếu tôi nhận được một đối tượng hoặc một mảng các đối tượng và tôi sử dụng kiểm tra kiểu bên trong để chuyển đổi một đối tượng thành danh sách một thành phần.
sastanin

14
Thay vào đó, sau đó chỉ cố gắng sử dụng phương thức ghi của nó, có những lúc bạn muốn làm điều này mà không gây ra ngoại lệ. Trong trường hợp này, bạn có thể làm ... if hasattr(ob, "write") and callable(ob.write): Hoặc lưu một số quyền truy cập chính tả ...func = getattr(ob, "write", None) if callable(func): ...
ideaman42

142
Gõ vịt là về việc sử dụng một thư viện. Kiểm tra loại là về việc viết một thư viện. Không phải cùng một vấn đề miền.
RickyA

16
@RickyA, tôi không đồng ý. Gõ vịt là về việc tương tác với các đối tượng bằng các giao diện với ngữ nghĩa nổi tiếng. Điều này có thể áp dụng cho mã thư viện hoặc mã sử dụng thư viện đó.
Dan Lenski

6
@ nyuszika7h, Trong Python3 hasattrchỉ supresses một AttributeError - Xem: docs.python.org/3.4/library/functions.html#hasattr
ideasman42

57

isinstance(o, str)sẽ trả về Truenếu olà một strhoặc thuộc loại kế thừa từ str.

type(o) is strsẽ trở lại Truenếu và chỉ khi olà một str. Nó sẽ trả về Falsenếu othuộc loại kế thừa str.


6
Tất nhiên, điều này sẽ thất bại nếu đối tượng không phải là một thể hiện của 'str', mà là một thứ gì đó giống như chuỗi. Giống như unicode, mmap, UserString hoặc bất kỳ loại nào do người dùng xác định. Cách tiếp cận thông thường trong Python không phải là đánh máy chữ.
Thomas Wouters

6
Bạn không cần phải xin lỗi, bạn có thể trả lời câu hỏi của mình. SO là cho câu trả lời, không phải là nghiệp.
Eli Bendersky

2
Điều này rất hữu ích. Bởi vì sự khác biệt giữa isinstancetype(var) == type('')không rõ ràng.
sastanin

30

Sau khi câu hỏi được hỏi và trả lời, loại gợi ý đã được thêm vào Python . Gợi ý nhập trong Python cho phép các loại được kiểm tra nhưng theo một cách rất khác với các ngôn ngữ được nhập tĩnh. Gợi ý nhập trong Python liên kết các loại đối số dự kiến ​​với các hàm như dữ liệu có thể truy cập thời gian chạy được liên kết với các hàm và điều này cho phép các loại được kiểm tra. Ví dụ về cú pháp gợi ý loại:

def foo(i: int):
    return i

foo(5)
foo('oops')

Trong trường hợp này, chúng tôi muốn một lỗi được kích hoạt foo('oops')vì loại đối số được chú thích là int. Gợi ý loại đã thêm không gây ra lỗi khi tập lệnh chạy bình thường. Tuy nhiên, nó thêm các thuộc tính cho hàm mô tả các loại dự kiến ​​mà các chương trình khác có thể truy vấn và sử dụng để kiểm tra lỗi loại.

Một trong những chương trình khác có thể được sử dụng để tìm lỗi loại là mypy:

mypy script.py
script.py:12: error: Argument 1 to "foo" has incompatible type "str"; expected "int"

(Bạn có thể cần cài đặt mypytừ trình quản lý gói của mình. Tôi không nghĩ rằng nó đi kèm với CPython nhưng dường như có một số mức độ "chính thức".)

Kiểm tra kiểu theo cách này khác với kiểm tra kiểu trong các ngôn ngữ được biên dịch nhập tĩnh. Vì các kiểu là động trong Python, nên việc kiểm tra kiểu phải được thực hiện trong thời gian chạy, điều này gây ra chi phí - ngay cả trên các chương trình chính xác - nếu chúng tôi khẳng định rằng điều đó xảy ra ở mọi cơ hội. Kiểm tra loại rõ ràng cũng có thể hạn chế hơn mức cần thiết và gây ra các lỗi không cần thiết (ví dụ: đối số có thực sự cần phải có listloại chính xác hay không có gì có thể lặp lại đủ không?).

Mặt trái của việc kiểm tra loại rõ ràng là nó có thể bắt lỗi sớm hơn và đưa ra thông báo lỗi rõ ràng hơn so với gõ vịt. Các yêu cầu chính xác của một loại vịt chỉ có thể được thể hiện bằng tài liệu bên ngoài (hy vọng nó kỹ lưỡng và chính xác) và lỗi từ các loại không tương thích có thể xảy ra ở xa nơi chúng bắt nguồn.

Gợi ý loại của Python có nghĩa là đưa ra một sự thỏa hiệp trong đó các loại có thể được chỉ định và kiểm tra nhưng không có chi phí bổ sung trong quá trình thực thi mã thông thường.

Các typingbiến gói Mời loại có thể được sử dụng trong loại gợi ý để diễn tả những hành vi cần thiết mà không cần loại cụ thể. Ví dụ, nó bao gồm các biến như IterableCallablecho các gợi ý để chỉ định nhu cầu cho bất kỳ loại nào với các hành vi đó.

Mặc dù gợi ý về kiểu là cách kiểm tra kiểu Pythonic nhiều nhất, nhưng thường thì Pythonic thậm chí còn không kiểm tra kiểu nào cả và dựa vào cách gõ vịt. Gợi ý loại tương đối mới và ban giám khảo vẫn chưa kết thúc khi chúng là giải pháp Pythonic nhất. Một so sánh tương đối không gây tranh cãi nhưng rất chung chung: Gợi ý loại cung cấp một dạng tài liệu có thể được thi hành, cho phép mã tạo ra các lỗi sớm hơn và dễ hiểu hơn, có thể bắt lỗi mà vịt không thể gõ và có thể được kiểm tra tĩnh (trong trường hợp bất thường ý nghĩa nhưng nó vẫn nằm ngoài thời gian chạy). Mặt khác, gõ vịt đã là cách Pythonic trong một thời gian dài, không áp đặt chi phí nhận thức của việc gõ tĩnh, ít dài dòng hơn và sẽ chấp nhận tất cả các loại khả thi và sau đó một số loại.


2
-1: mypy đặc biệt tự gọi mình là "trình kiểm tra kiểu tĩnh" vì vậy tôi không chắc chắn nơi bạn có "kiểm tra loại phải được thực hiện trong thời gian chạy" từ đó.
Kevin

@Kevin Nhìn lại, đó là một sự lạc đề không cần thiết, nhưng để hiểu sâu hơn, các gợi ý kiểu của Python được chuyển thành dữ liệu thời gian chạy và mypylà một mô-đun Python sử dụng importlibđể truy cập dữ liệu đó. Cho dù đây là "kiểm tra kiểu tĩnh" là một câu hỏi triết học nhưng nó khác với những gì hầu hết mong đợi vì trình thông dịch ngôn ngữ thông thường và máy móc nhập khẩu có liên quan.
Praxeolitic

4
Điều đó cũng không đúng. Nó sử dụng typed_ast, bản thân nó chỉ là một bản sao của ast với các tính năng bổ sung. ast không nhập mô-đun; nó phân tích chúng thành một cây cú pháp trừu tượng.
Kevin

18

Dưới đây là một ví dụ tại sao gõ vịt là xấu xa mà không biết khi nào nó nguy hiểm. Ví dụ: Đây là mã Python (có thể bỏ qua thụt lề thích hợp), lưu ý rằng tình huống này có thể tránh được bằng cách quan tâm đến các hàm isinstance và hiện tại để đảm bảo rằng khi bạn thực sự cần một con vịt, bạn sẽ không nhận được bom.

class Bomb:
    def __init__(self):
        ""

    def talk(self):
        self.explode()

    def explode(self):
        print "BOOM!, The bomb explodes."

class Duck:
    def __init__(self):
        ""
    def talk(self):
        print "I am a duck, I will not blow up if you ask me to talk."    

class Kid:
    kids_duck = None

    def __init__(self):
        print "Kid comes around a corner and asks you for money so he could buy a duck."

    def takeDuck(self, duck):
        self.kids_duck = duck
        print "The kid accepts the duck, and happily skips along"

    def doYourThing(self):
        print "The kid tries to get the duck to talk"
        self.kids_duck.talk()

myKid = Kid()
myBomb = Bomb()
myKid.takeDuck(myBomb)
myKid.doYourThing()

36
Ngay cả khi kiểm tra kiểu, bạn có thể tạo class EvilDuck(Duck)và ghi đè cuộc nói chuyện (). Hoặc nhiều khả năng, class ChineseCancerDuck(Duck)với một tác dụng phụ khó chịu không xuất hiện cho đến nhiều năm sau đó. Bạn sẽ tốt hơn nếu chỉ giám sát con bạn (và kiểm tra kỹ lưỡng đồ chơi của nó :)
Brett Thomas

36
Bom không nói chuyện. Đừng thêm các phương pháp vô nghĩa và điều này sẽ không xảy ra.
đúng

7
@Dmitry, đây là lời chỉ trích phổ biến về Duck Typing: en.wikipedia.org/wiki/Duck_typing#Criticism ... về cơ bản bạn đang nói rằng bất kỳ giao diện nào mà ngữ nghĩa không được thi hành bởi ngôn ngữ là xấu. Tôi tin rằng đây là cách tiếp cận của Java nhiều hơn. Điểm chung của việc gõ vịt của Python là nó chỉ hoạt động khi có một quy ước thường được đề cao về ý nghĩa của các giao diện cụ thể. Ví dụ: bạn có thể xử lý nhiều mã Python bằng cách ghi đè __file__thuộc tính (thường được sử dụng để xác định các đối tượng giống như tệp) để có nghĩa khác.
Dan Lenski

2
Tất cả điều này bắt nguồn từ câu nói đùa cũ "Bác sĩ, thật đau khi tôi làm điều này." ... "Vậy thì đừng làm thế." Không thỏa mãn với ai đó đã quen với "nếu nó biên dịch, nó chạy", nhưng đó là lý do tại sao thử nghiệm nỗi ám ảnh phát triển ra khỏi thế giới ngôn ngữ năng động.
clacke

1
@clacke về cơ bản, quá tốn kém để thực thi các loại trong thời gian chạy một cách nghiêm ngặt bởi vì MỌI THỨ phải là một đối tượng (để ánh xạ từ chuỗi sang bất kỳ loại nào có thể) và quá thuận tiện để không bị nhúng bởi vì vịt cho phép các kỹ thuật tạo mẫu thực sự mạnh mẽ vượt qua mọi thứ. thường rất khó thực hiện với các giao diện cứng nhắc. Bên cạnh đó, bất kỳ ngôn ngữ tĩnh nào cũng phải đối mặt với một điểm mà nó cần tạo ra cách gõ vịt thông qua các thư viện động, đánh giá và xâu chuỗi hoặc giao diện, và những điều này vốn không làm cho nó trở nên xấu xa, chỉ rất mạnh mẽ.
Dmitry

12
isinstance(o, str)

Liên kết đến tài liệu


1
Mặc dù liên kết này có thể trả lời câu hỏi, tốt hơn là bao gồm các phần thiết yếu của câu trả lời ở đây và cung cấp liên kết để tham khảo. Câu trả lời chỉ liên kết có thể trở nên không hợp lệ nếu trang được liên kết thay đổi.
EKons

7

Tôi nghĩ điều thú vị khi sử dụng một ngôn ngữ động như Python là bạn thực sự không cần phải kiểm tra thứ gì đó như thế.

Tôi sẽ chỉ gọi các phương thức cần thiết trên đối tượng của bạn và bắt một AttributeError. Sau này, điều này sẽ cho phép bạn gọi các phương thức của mình với các đối tượng khác (dường như không liên quan) để thực hiện các nhiệm vụ khác nhau, chẳng hạn như chế nhạo một đối tượng để thử nghiệm.

Tôi đã sử dụng điều này rất nhiều khi lấy dữ liệu ra khỏi web urllib2.urlopen()và trả về một tệp giống như đối tượng. Điều này có thể lần lượt có thể được truyền cho hầu hết mọi phương thức đọc từ một tệp, bởi vì nó thực hiện cùng một read()phương thức như một tệp thực.

Nhưng tôi chắc chắn rằng có một thời gian và địa điểm để sử dụng isinstance(), nếu không nó có thể sẽ không ở đó :)


Một ví dụ điển hình khi bạn phải sử dụng nó là nếu bạn đang phân tích cú pháp một đối tượng json động. Bạn không biết trước nếu một trường là một chuỗi hoặc một từ điển.
Xám

6

Để biết thêm validations loại phức tạp tôi như typeguard 's phương pháp chứng thực dựa trên loại trăn chú thích gợi ý:

from typeguard import check_type
from typing import List

try:
    check_type('mylist', [1, 2], List[int])
except TypeError as e:
    print(e)

Bạn có thể thực hiện các xác nhận rất phức tạp theo cách rất sạch sẽ và dễ đọc.

check_type('foo', [1, 3.14], List[Union[int, float]])
# vs
isinstance(foo, list) and all(isinstance(a, (int, float)) for a in foo) 

6

Bạn có thể kiểm tra loại biến bằng cách sử dụng __name__ của một loại.

Ví dụ:

>>> a = [1,2,3,4]  
>>> b = 1  
>>> type(a).__name__
'list'
>>> type(a).__name__ == 'list'
True
>>> type(b).__name__ == 'list'
False
>>> type(b).__name__
'int'

Cảm ơn, đây là mã bí mật tôi muốn khi tôi hiển thị nó dưới dạng phản hồi cho người dùng. Mất quá lâu để tìm thấy điều này ...
Aaron D. Marasco

5

Tới Hugo:

Có thể bạn muốn nói listđúng hơn array, nhưng điều đó chỉ ra toàn bộ vấn đề với việc kiểm tra kiểu - bạn không muốn biết liệu đối tượng trong câu hỏi có phải là một danh sách hay không, bạn muốn biết đó là một loại trình tự hay nếu đó là một đối tượng. Vì vậy, hãy cố gắng sử dụng nó như một chuỗi.

Giả sử bạn muốn thêm đối tượng vào một chuỗi hiện có hoặc nếu đó là một chuỗi các đối tượng, hãy thêm tất cả chúng

try:
   my_sequence.extend(o)
except TypeError:
  my_sequence.append(o)

Một mẹo nhỏ với điều này là nếu bạn đang làm việc với các chuỗi và / hoặc chuỗi các chuỗi - đó là khó khăn, vì một chuỗi thường được coi là một đối tượng, nhưng đó cũng là một chuỗi các ký tự. Tệ hơn thế, vì nó thực sự là một chuỗi các chuỗi có độ dài đơn.

Tôi thường chọn thiết kế API của mình để nó chỉ chấp nhận một giá trị duy nhất hoặc một chuỗi - nó làm cho mọi thứ dễ dàng hơn. Không khó để đặt một [ ]giá trị xung quanh bạn khi bạn vượt qua nó nếu cần.

(Mặc dù điều này có thể gây ra lỗi với các chuỗi, vì chúng trông giống như (là) chuỗi.)


0

Một cách đơn giản để kiểm tra loại là so sánh nó với loại mà bạn biết.

>>> a  = 1
>>> type(a) == type(1)
True
>>> b = 'abc'
>>> type(b) == type('')
True

-1

Tôi nghĩ rằng cách tốt nhất là gõ tốt các biến của bạn. Bạn có thể làm điều này bằng cách sử dụng thư viện "gõ".

Thí dụ:

from typing import NewType UserId = NewType ('UserId', int) some_id = UserId (524313)

Xem https://docs.python.org/3/l Library / type.html


-7

Bạn có thể kiểm tra với dòng dưới đây để kiểm tra loại ký tự nào có giá trị đã cho là:

def chr_type(chrx):
    if chrx.isalpha()==True:
        return 'alpha'
    elif chrx.isdigit()==True:
        return 'numeric'
    else:
        return 'nothing'

chr_type("12)

3
Bạn có chắc chắn không muốn xóa câu trả lời này @Venkatesan?
Xám
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.