यह निर्धारित करने का उचित तरीका क्या है कि कोई वस्तु पायथन जैसी वस्तु है?


94

मेरे पास कोड है जो उम्मीद करता है strलेकिन bytesनिम्नलिखित तरीके से पारित होने के मामले को संभाल लेगा :

if isinstance(data, bytes):
    data = data.decode()

दुर्भाग्य से, इस मामले में काम नहीं करता है bytearray। क्या यह परीक्षण करने का एक अधिक सामान्य तरीका है कि क्या कोई वस्तु bytesया तो है bytearray, या मुझे सिर्फ दोनों की जांच करनी चाहिए? क्या यह hasattr('decode')उतना बुरा है जितना मुझे लगता है कि यह होगा?


6
व्यक्तिगत रूप से, मुझे अजगर के बतख को अगले लड़के जितना ही पसंद है। लेकिन अगर आपको अपने इनपुट तर्कों पर जाँच करने और विभिन्न प्रकारों के लिए ज़बरदस्ती करने की आवश्यकता है, तो आप अब टाइप नहीं कर रहे हैं - आप केवल एक कोड को पढ़ने के लिए अपने कोड को और अधिक कठिन बना रहे हैं। यहां मेरा सुझाव (और अन्य लोग असहमत हो सकते हैं) कई कार्य करने के लिए होंगे (जो कि प्रकार के ज़बरदस्ती को संभालते हैं और आधार कार्यान्वयन के लिए प्रतिनिधि होते हैं)।
— एमजिल्सन

(1) जब तक आपको विरासत 2 पायथन के साथ संगतता के लिए इसकी आवश्यकता न हो; पाठ और बाइनरी डेटा दोनों को एक साथ स्वीकार करने से बचें। यदि आपका फ़ंक्शन पाठ के साथ काम करता है तो इसे केवल स्वीकार करना चाहिए str। कुछ अन्य कोड को बाइट्स से युनिकोड पर इनपुट में जल्द से जल्द बदलना चाहिए। (2) "बाइट्स-
— लाइक

मुख्य समस्या यह है कि यह फ़ंक्शन पायथन 2 में काम नहीं करता है, जहां एक साधारण ASCII स्ट्रिंग <बाइट्स> का परीक्षण पास करता है!
— Apostolos

जवाबों:


76

यहाँ आप उपयोग कर सकते हैं कुछ दृष्टिकोण हैं।

बतख टाइपिंग

चूंकि पायथन को बतख टाइप किया गया है , आप बस इस प्रकार कर सकते हैं (जो आमतौर पर सुझाए गए तरीके से लगता है):

try:
    data = data.decode()
except (UnicodeDecodeError, AttributeError):
    pass

आप के hasattrरूप में आप का उपयोग कर सकते हैं , हालांकि, और यह शायद ठीक हो जाएगा। यह निश्चित रूप से, .decode()किसी दिए गए ऑब्जेक्ट के लिए विधि को संभालने के लिए एक स्ट्रिंग देता है, और इसका कोई बुरा दुष्प्रभाव नहीं है।

मैं व्यक्तिगत रूप से या तो अपवाद या hasattrविधि की सिफारिश करता हूं , लेकिन आप जो भी उपयोग करते हैं वह आपके ऊपर है।

Str () का उपयोग करें

यह दृष्टिकोण असामान्य है, लेकिन संभव है:

data = str(data, "utf-8")

अन्य एन्कोडिंग स्वीकार्य हैं, जैसे बफर प्रोटोकॉल के साथ .decode()। त्रुटि से निपटने के लिए आप तीसरा पैरामीटर भी पास कर सकते हैं।

एकल-प्रेषण सामान्य कार्य (पायथन 3.4+)

अजगर 3.4 और इसके बाद के संस्करण एकल प्रेषण सामान्य कार्यों नामक एक गंधा सुविधा है, के माध्यम से शामिल functools.singledispatch । यह थोड़ी अधिक क्रिया है, लेकिन यह अधिक स्पष्ट है:

def func(data):
    # This is the generic implementation
    data = data.decode()
    ...

@func.register(str)
def _(data):
    # data will already be a string
    ...

यदि आप ऐसा चुनते हैं तो आप वस्तुओं bytearrayऔर bytesवस्तुओं के लिए विशेष हैंडलर भी बना सकते हैं ।

खबरदार : एकल-प्रेषण फ़ंक्शन केवल पहले तर्क पर काम करते हैं! यह एक जानबूझकर विशेषता है, PEP 433 देखें ।


एकल-प्रेषण जनक के उल्लेख के लिए +1, जिसे मैं प्रदान की गई मानक लाइब्रेरी को पूरी तरह से भूल गया।
— ए। विलकॉक्स

चूंकि str पर फोन करना कुछ नहीं करता है, और मुझे सबसे स्पष्ट लग रहा था, मैं उसी के साथ चला गया।
— ए। विलकॉक्स

कुल मिलाकर मैं hasattrकोशिश से ज्यादा पसंद करता हूं / आपको छोड़कर किसी को गलती से डिकोड फ़ंक्शन में कुछ बग निगलने से रोकता है, लेकिन +1।
— केर्डसन

39

आप उपयोग कर सकते हैं:

isinstance(data, (bytes, bytearray))

विभिन्न आधार वर्ग के कारण यहाँ उपयोग किया जाता है।

>>> bytes.__base__
<type 'basestring'>
>>> bytearray.__base__
<type 'object'>

जाँच करने के लिए bytes

>>> by = bytes()
>>> isinstance(by, basestring)
True

तथापि,

>>> buf = bytearray()
>>> isinstance(buf, basestring)
False

उपरोक्त कोड अजगर 2.7 के तहत परीक्षण किए गए हैं

दुर्भाग्य से, अजगर 3.4 के तहत, वे समान हैं ...।

>>> bytes.__base__
<class 'object'>
>>> bytearray.__base__
<class 'object'>

1
छह.स्ट्रिंग_टाइप्स 2/3 संगत होना चाहिए।
— जोशुआ ओल्सन

पायथन 2 में इस तरह की जाँच काम नहीं करती है, जहाँ एक साधारण ASCII स्ट्रिंग <बाइट्स> का टेस्ट पास करती है!
— Apostolos

13
>>> content = b"hello"
>>> text = "hello"
>>> type(content)
<class 'bytes'>
>>> type(text)
<class 'str'>
>>> type(text) is str
True
>>> type(content) is bytes
True

ध्यान दें कि यह पायथन 2 में एक विश्वसनीय परीक्षण नहीं है , जहां एक स्ट्रिंग ऑब्जेक्ट बाइट्स के रूप में भी गुजरता है! यानी उपरोक्त कोड के आधार पर, type(text) is bytesयह सच होगा!
— अपोस्टोलोस

11

यह कोड तब तक सही नहीं है जब तक आप कुछ ऐसा नहीं जानते जो हम नहीं करते:

if isinstance(data, bytes):
    data = data.decode()

आप के एन्कोडिंग को नहीं जानते (प्रकट करते हैं) data। आप मान रहे हैं कि यह UTF-8 है , लेकिन यह बहुत अच्छी तरह से गलत हो सकता है। चूंकि आपको एन्कोडिंग का पता नहीं है, इसलिए आपके पास टेक्स्ट नहीं है । आपके पास बाइट्स हैं, जिनका सूरज के नीचे कोई अर्थ हो सकता है।

अच्छी खबर यह है कि बाइट्स के सबसे यादृच्छिक क्रम वैध UTF-8 नहीं हैं, इसलिए जब यह टूट जाता है, तो यह errors='strict'चुपचाप गलत काम करने के बजाय जोर से टूट जाएगा ( डिफ़ॉल्ट है)। इससे भी अच्छी खबर यह है कि उन यादृच्छिक क्रमों में से अधिकांश जो वैध यूटीएफ -8 के होते हैं, वे भी मान्य ASCII हैं, जो ( लगभग ) हर किसी को वैसे भी पार्स करने के लिए सहमत हैं।

बुरी खबर यह है कि इसे ठीक करने का कोई उचित तरीका नहीं है। एन्कोडिंग जानकारी प्रदान करने का एक मानक तरीका है: strइसके बजाय का उपयोग करें bytes। अगर कुछ तृतीय-पक्ष कोड आप एक हाथ bytesया bytearrayवस्तु किसी भी आगे संदर्भ या जानकारी के बिना, केवल सही कार्रवाई विफल है।


अब, आप एन्कोडिंग जानते हैं, तो आप functools.singledispatchयहाँ उपयोग कर सकते हैं :

@functools.singledispatch
def foo(data, other_arguments, ...):
    raise TypeError('Unknown type: '+repr(type(data)))

@foo.register(str)
def _(data, other_arguments, ...):
    # data is a str

@foo.register(bytes)
@foo.register(bytearray)
def _(data, other_arguments, ...):
    data = data.decode('encoding')
    # explicit is better than implicit; don't leave the encoding out for UTF-8
    return foo(data, other_arguments, ...)

यह तरीकों पर काम नहीं करता है, और dataपहला तर्क होना चाहिए। यदि वे प्रतिबंध आपके लिए काम नहीं करते हैं, तो अन्य उत्तरों में से एक का उपयोग करें।


इस विशिष्ट विधि के लिए मैं जो पुस्तकालय लिख रहा हूं, मैं निश्चित रूप से जानता हूं कि मुझे जो बाइट्स और / या मैं प्राप्त कर रहा हूं, वह यूटीएफ -8 एनकोडेड है।
— ए। विलकॉक्स

1
@AndrewWilcox: काफी साफ है, लेकिन मैं इस जानकारी को भविष्य के Google ट्रैफ़िक के लिए छोड़ रहा हूं।
— केविन

4

यह निर्भर करता है कि आप क्या हल करना चाहते हैं। यदि आप एक ही कोड चाहते हैं जो दोनों मामलों को एक स्ट्रिंग में परिवर्तित करता है, तो आप बस प्रकार को bytesपहले में बदल सकते हैं , और फिर डिकोड कर सकते हैं। इस तरह, यह एक लाइनर है:

#!python3

b1 = b'123456'
b2 = bytearray(b'123456')

print(type(b1))
print(type(b2))

s1 = bytes(b1).decode('utf-8')
s2 = bytes(b2).decode('utf-8')

print(s1)
print(s2)

इस तरह, आपके लिए उत्तर हो सकता है:

data = bytes(data).decode()

वैसे भी, मैं 'utf-8'डिकोड को स्पष्ट रूप से लिखने का सुझाव देता हूं , अगर आप कुछ बाइट्स को छोड़ना नहीं चाहते हैं। कारण यह है कि अगली बार जब आप या कोई अन्य व्यक्ति स्रोत कोड पढ़ेगा, तो स्थिति अधिक स्पष्ट होगी।


3

यहां दो प्रश्न हैं, और उनके उत्तर अलग-अलग हैं।

पहला सवाल, इस पोस्ट का शीर्षक, यह निर्धारित करने का उचित तरीका है कि क्या पायथन में कोई वस्तु बाइट्स जैसी वस्तु है? इस प्रकार में निर्मित की एक संख्या शामिल है ( bytes, bytearray, array.array, memoryview, दूसरों को?) और संभवतः भी उपयोगकर्ता परिभाषित प्रकार के। सबसे अच्छा तरीका है कि मैं इन के लिए जाँच करने के लिए memoryviewपता है उनमें से एक बनाने की कोशिश है :

>>> memoryview(b"foo")
<memory at 0x7f7c43a70888>
>>> memoryview(u"foo")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: memoryview: a bytes-like object is required, not 'str'

मूल पोस्ट के शरीर में, हालांकि, ऐसा लगता है कि प्रश्न के बजाय यह है कि मैं कैसे परीक्षण करूं कि कोई वस्तु डिकोड () का समर्थन करती है? इस प्रश्न के उत्तर के ऊपर @ एलिज़बेथ-मायर्स महान है। ध्यान दें कि सभी बाइट्स जैसी वस्तुएं डिकोड () का समर्थन नहीं करती हैं।


1
ध्यान दें कि यदि आप ऐसा करते हैं, तो आपको कॉल करना होगा .release(), या संदर्भ प्रबंधक संस्करण का उपयोग करना होगा ।
— o 11c

मुझे लगता है कि सीपीथॉन में अस्थायी memoryviewको तुरंत मुक्त कर दिया .release()जाएगा और इसे संक्षेप में कहा जाएगा। लेकिन मैं मानता हूं कि यह उस पर भरोसा नहीं करना सबसे अच्छा है, क्योंकि सभी पायथन कार्यान्वयन संदर्भ-गणना नहीं हैं।
— जैक ओ'कॉनर

0

परीक्षण if isinstance(data, bytes)या if type(data) == bytesआदि, अजगर 2 में काम नहीं करता है, जहां एक साधारण ASCII स्ट्रिंग का परीक्षण पास होता है! क्योंकि मैं पायथन 2 और पायथन 3 दोनों का उपयोग करता हूं, इसे दूर करने के लिए मैं निम्नलिखित जांच करता हूं:

if str(type(data)).find("bytes") != -1: print("It's <bytes>")

यह थोड़ा बदसूरत है, लेकिन यह वह काम करता है जो सवाल पूछता है और यह हमेशा काम करता है, सबसे सरल तरीके से।


को Python2 strवस्तुओं रहे हैं bytes : हालांकि str is bytes> - Trueको Python2 में
— snakecharmerb

जाहिर है, इसलिए पता लगाने की समस्या! :)
— Apostolos
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.