python - والرجيم - معرفة شكل الجسم من الوزن والطول




تحديد نوع الجسم؟ (6)

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


تحديد نوع كائن بايثون

تحديد نوع كائن type

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

على الرغم من أنه يعمل ، تجنب صفات تسطير مزدوجة مثل __class__ - وهي ليست عامة بشكل علني ، وعلى الرغم من أنه ليس في هذه الحالة ، فإن وظائف المضمن عادة ما يكون لها سلوك أفضل.

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

اكتب التحقق

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

حسنا هذا سؤال مختلف ، لا تستخدم النوع - استخدام 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)

يغطي هذا الحالة التي قد يقوم فيها المستخدم بعمل شيء ذكي أو معقول من قِبل str -subclassing - وفقًا لمبدأ Liskov Substitution ، فأنت تريد أن تكون قادرًا على استخدام مثيلات فئة فرعية دون كسر التعليمات البرمجية - و isinstance يدعم هذا.

استخدم التجريد

والأفضل من ذلك ، قد تبحث عن فئة أساسية محددة من collections أو 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)

أو فقط لا تدقق بصراحة

أو ربما الأفضل من كل شيء ، استخدم كتابة البطة ، ولا تكتب صراحةً رمزك. تدعم كتابة البطة استبدال Liskov مع المزيد من الأناقة والقيمة الأقل.

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)

استنتاج

  • استخدم type للحصول بالفعل على فئة مثيل.
  • استخدم isinstance لفحص صريح isinstance الفرعية الفعلية أو abstractions المسجلة.
  • ومجرد تجنب التحقق من النوع حيث يكون منطقيًا.

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

هنا أحاول الإجابة عن سؤال العنوان: كيف يمكنني تحديد نوع الكائن التعسفي ؟ اقتراحات أخرى حول استخدام أو عدم استخدام isinstance جيدة في العديد من التعليقات والأجوبة ، لكنني لا أوجه هذه المخاوف.

تتمثل المشكلة الرئيسية في أسلوب type() أنه لا يعمل بشكل صحيح مع مثيلات النمط القديم :

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

سينتج عن تنفيذ هذا المقتطف ما يلي:

Are o and t instances of the same class? True

وأنا أقول إن هذا ليس ما يتوقعه معظم الناس.

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

هذا هو أصغر مقتطف من الشفرة التي يمكنني التفكير فيها والتي تفي بهذا السؤال الشرعي بطريقة متسقة:

#!/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

قد يكون استخدام Pythonic أكثر من try ... except الحظر. وبهذه الطريقة ، إذا كان لديك فصل يشبه قائمة ، أو حركات مثل dict ، فإنها سوف تتصرف بشكل صحيح بغض النظر عن نوعه بالفعل .

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

المشكلة الأخرى type(A) is type(B) التحقق من type(A) is type(B) أنه إذا كانت A هي فئة فرعية من B ، فإنها تقوم بالتقييم إلى false عندما ، برمجيًا ، كنت تأمل أن يكون ذلك true . إذا كان الكائن عبارة عن فئة فرعية من القائمة ، فيجب أن يعمل مثل قائمة: فالتحقق من النوع كما هو موضح في الإجابة الأخرى سيمنع ذلك. (ستعمل isinstance ، ولكن).


كن حذرا باستخدام isinstance

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

لكن اكتب

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

وكملاحظة للأجوبة السابقة ، تجدر الإشارة إلى وجود collections.abc التي تحتوي على العديد من الطبقات الأساسية التجريدية (ABCs) التي تكمل كتابة البط.

على سبيل المثال ، بدلاً من التحقق صراحةً من وجود شيء ما مع قائمة:

isinstance(my_obj, list)

يمكنك ، إذا كنت مهتمًا فقط بمعرفة ما إذا كان الكائن الذي لديك يسمح لك بالحصول على العناصر ، فاستخدم collections.abc.Sequence :

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

إذا كنت مهتمًا تمامًا بالأشياء التي تسمح بالحصول على العناصر أو إعدادها وحذفها (أي تسلسلات قابلة للتغيير ) ، فستختار collections.abc.MutableSequence . collections.abc.MutableSequence .

يتم تعريف العديد من ABCs الأخرى هناك ، Mapping للأشياء التي يمكن استخدامها كخرائط ، Iterable ، Callable ، وما إلى ذلك. يمكن الاطلاع على قائمة كاملة بجميع هذه الوثائق في collections.abc .


يمكنك استخدام type() أو isinstance() .

>>> type([]) is list
True

حذر من أنه بإمكانك كشف list أو أي نوع آخر عن طريق تعيين متغير في النطاق الحالي لنفس الاسم.

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

أعلاه نلاحظ أن يتم نقل dict إلى سلسلة ، وبالتالي فإن الاختبار:

type({}) is dict

... فشل.

للتغلب على هذا واستخدام type() أكثر حذرا:

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




python