it-swarm.asia

ما هي الطريقة المعتادة للتحقق من النوع في بيثون؟

ما هي أفضل طريقة للتحقق مما إذا كان كائن معين من نوع معين؟ ماذا عن التحقق مما إذا كان الكائن يرث من نوع معين؟

دعنا نقول لدي كائن o. كيف يمكنني التحقق مما إذا كان اسم str؟

1067
Herge

للتحقق مما إذا كان o هو مثيل str أو أي فئة فرعية لـ str ، استخدم isinstance (ستكون هذه هي الطريقة "المتعارف عليها"):

if isinstance(o, str):

للتحقق مما إذا كان نوع ostr تمامًا (باستثناء الفئات الفرعية):

if type(o) is str:

يعمل ما يلي أيضًا ، وقد يكون مفيدًا في بعض الحالات:

if issubclass(type(o), str):

راجع الوظائف المدمجة في مكتبة Python Reference للحصول على المعلومات ذات الصلة.

ملاحظة أخرى: في هذه الحالة ، إذا كنت تستخدم python 2 ، فقد تحتاج بالفعل إلى استخدام:

if isinstance(o, basestring):

لأن هذا سيحقق أيضًا سلاسل Unicode ( unicode ليس فئة فرعية من str ؛ كلا str و unicode هما فئتان فرعيتان من basestring ). لاحظ أن basestring لم يعد موجودًا في الثعبان 3 ، حيث يوجد فصل صارم من السلاسل ( str ) والبيانات الثنائية ( bytes ).

بدلاً من ذلك ، isinstance يقبل مجموعة من الفصول. سيؤدي ذلك إلى إرجاع True إذا كانت x هي مثيل لأي فئة فرعية لأي من (str، unicode):

if isinstance(o, (str, unicode)):
1303
Fredrik Johansson

معظم طريقة Pythonic للتحقق من نوع كائن ... لا للتحقق من ذلك.

بما أن Python تشجع Duck Typing ، يجب عليك فقط try...except لاستخدام أساليب الكائن بالطريقة التي تريد استخدامها. لذلك إذا كانت وظيفتك تبحث عن كائن ملف قابل للكتابة ، لا تأكد من أنها فئة فرعية من file ، فقط حاول استخدام طريقة .write()!

بالطبع ، في بعض الأحيان تنهار هذه التجريدات الجميلة و isinstance(obj, cls) هو ما تحتاجه. ولكن استخدم لماما.

172
Dan Lenski

isinstance(o, str) ستُرجع True إذا ostr أو من النوع الذي يرث من str.

type(o) is str ستعود True إذا وفقط إذا o هي str. سيعود False إذا o من النوع الذي يرث من str.

46
Herge

بعد طرح السؤال والإجابة عليه ، تمت إضافة تلميحات type إلى Python . تتيح تلميحات الكتابة في Python فحص الأنواع ولكن بطريقة مختلفة تمامًا عن اللغات المكتوبة بشكل ثابت. تقوم تلميحات الكتابة في Python بربط الأنواع المتوقعة من الوسائط بالوظائف كبيانات يمكن الوصول إليها في وقت التشغيل مرتبطة بالوظائف وهذا يسمح لأنواع يتم فحصها. مثال على بناء جملة تلميح:

def foo(i: int):
    return i

foo(5)
foo('oops')

في هذه الحالة ، نريد أن يتم تشغيل خطأ في foo('oops') لأن النوع المشروح للوسيطة int. تلميح نوع المضافة لا تسبب خطأ في حدوث عندما يتم تشغيل البرنامج النصي بشكل طبيعي. ومع ذلك ، فإنه يضيف سمات إلى الوظيفة التي تصف الأنواع المتوقعة التي يمكن للبرامج الأخرى الاستعلام عنها واستخدامها للتحقق من أخطاء الكتابة.

أحد هذه البرامج الأخرى التي يمكن استخدامها للعثور على الخطأ type هو mypy:

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

(قد تحتاج إلى تثبيت mypy من مدير الحزم الخاص بك. لا أعتقد أن الأمر يأتي مع CPython ولكن يبدو أنه يتمتع بمستوى "رسمي".)

يختلف التحقق من النوع بهذه الطريقة عن التحقق من الكتابة بلغات مترجمة بشكل ثابت. نظرًا لأن الأنواع ديناميكية في Python ، يجب إجراء فحص الكتابة في وقت التشغيل ، مما يفرض تكلفة - حتى على البرامج الصحيحة - إذا أصررنا على حدوث ذلك في كل فرصة. قد تكون عمليات تدقيق الكتابة الصريحة أكثر تقييدًا مما هو مطلوب وتتسبب في حدوث أخطاء غير ضرورية (على سبيل المثال ، هل تحتاج الوسيطة بالفعل إلى أن تكون من نوع list بالضبط أم أنها كافية لأي شيء؟).

يتمثل الجانب العلوي من التحقق الصريح من النوعية في أنه يمكنه التقاط الأخطاء مسبقًا وإعطاء رسائل خطأ أوضح من كتابة البط. لا يمكن التعبير عن المتطلبات الدقيقة لنوع البط إلا من خلال وثائق خارجية (نأمل أن تكون دقيقة ودقيقة) ويمكن أن تحدث أخطاء من أنواع غير متوافقة بعيدًا عن المكان الذي نشأت فيه.

تهدف تلميحات بايثون إلى تقديم حل وسط حيث يمكن تحديد الأنواع والتحقق منها ولكن لا توجد تكلفة إضافية أثناء تنفيذ التعليمات البرمجية المعتادة.

توفر الحزمة typing متغيرات الكتابة التي يمكن استخدامها في تلميحات الكتابة للتعبير عن السلوكيات المطلوبة دون الحاجة إلى أنواع معينة. على سبيل المثال ، يتضمن متغيرات مثل Iterable و Callable للحصول على تلميحات لتحديد الحاجة لأي نوع مع تلك السلوكيات.

في حين أن تلميحات الكتابة هي أكثر طريقة بيثونية لفحص الأنواع ، فغالبًا ما يكون بيثونيك أكثر عدم التحقق من الأنواع على الإطلاق والاعتماد على كتابة البط. تلميحات الكتابة جديدة نسبيًا ولا تزال هيئة المحلفين في وضع التشغيل عندما تكون أكثر حلول Pythonic. مقارنة غير مثيرة للجدل نسبيًا ولكنها عامة جدًا: توفر تلميحات الكتابة نوعًا من الوثائق التي يمكن إنفاذها ، وتسمح بإنشاء الشفرة في وقت مبكر وأسهل لفهم الأخطاء ، ويمكنها اكتشاف الأخطاء التي لا يمكن للكتابة البطة ، ويمكن التحقق منها بشكل ثابت (بطريقة غير عادية) الشعور لكنه لا يزال خارج وقت التشغيل). من ناحية أخرى ، كانت كتابة البطة هي الطريقة البيثونية لفترة طويلة ، ولا تفرض الحمل المعرفي للكتابة الساكنة ، وهي أقل مطبوعًا ، وستقبل جميع الأنواع القابلة للحياة ، ثم بعضها.

22
Praxeolitic

هنا مثال على السبب في أن كتابة البطة شريرة دون معرفة متى يكون ذلك خطيرًا. على سبيل المثال: فيما يلي رمز Python (ربما يتم حذف المسافة البادئة المناسبة) ، لاحظ أن هذا الموقف يمكن تجنبه عن طريق الاهتمام بوظائف isinstance و issubclassof للتأكد من أنك عندما تحتاج حقًا إلى بطة ، لن تحصل على قنبلة.

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()
16
Dmitry
12
Alexander Kojevnikov

أعتقد أن الشيء اللطيف حول استخدام لغة ديناميكية مثل بيثون هو أنه لا يجب عليك التحقق من شيء من هذا القبيل.

أود فقط استدعاء الأساليب المطلوبة على كائنك والتقاط AttributeError. فيما بعد ، سيسمح لك ذلك باستدعاء طرقك مع كائنات أخرى (غير مرتبطة فيما يبدو) لإنجاز مهام مختلفة ، مثل الاستهزاء بكائن للاختبار.

لقد استخدمت هذا كثيرًا عند الحصول على البيانات من الويب باستخدام urllib2.urlopen() والتي تقوم بإرجاع ملف مثل كائن. يمكن تمرير هذا بدوره إلى أي طريقة تقريبًا تقريبًا من ملف ، لأنها تطبق نفس طريقة read() كملف حقيقي.

لكنني متأكد من أنه يوجد وقت ومكان لاستخدام isinstance() ، وإلا فلن يكون هناك على الأرجح :)

6
Will Harding

إلى هوغو:

من المحتمل أنك تعني list بدلاً من array ، لكن هذا يشير إلى المشكلة برمتها مع التحقق من الكتابة - لا تريد أن تعرف إذا كان الكائن المعني عبارة عن قائمة ، فأنت تريد أن تعرف ما إذا كان نوعًا من التسلسل أم أنه مفرد موضوع. لذا حاول استخدامه مثل التسلسل.

قل أنك تريد إضافة الكائن إلى تسلسل موجود ، أو إذا كان تسلسلًا للكائنات ، فأضفها جميعًا

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

إحدى الخدع في هذا الأمر هي إذا كنت تعمل باستخدام سلاسل و/أو تسلسلات سلاسل - هذا أمر صعب ، حيث يُعتقد أن السلسلة غالبًا ما تكون ككائن واحد ، ولكنها أيضًا سلسلة من الأحرف. أسوأ من ذلك ، لأنه في الحقيقة سلسلة من السلاسل ذات الطول الواحد.

عادةً ما أختار تصميم واجهة برمجة التطبيقات (API) الخاصة بي بحيث لا يقبل إلا قيمة واحدة أو تسلسلًا - مما يجعل الأمور أسهل. ليس من الصعب وضع [ ] حول القيمة الفردية عند تمريرها إذا لزم الأمر.

(على الرغم من أن هذا قد يتسبب في حدوث أخطاء بالسلاسل ، كما يبدو (تسلسل) (.

4
Chris Barker

لمزيد من عمليات التحقق من النوع الأكثر تعقيدًا ، أحب أسلوب typeguard / التحقق من الصحة استنادًا إلى التعليقات التوضيحية تلميح نوع بايثون:

from typeguard import check_type
from typing import List

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

يمكنك إجراء عمليات التحقق المعقدة للغاية بأسلوب نظيف للغاية ويمكن قراءته.

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