पायथन मल्टीलाइन स्ट्रिंग्स के लिए उचित इंडेंटेशन


456

एक फ़ंक्शन के भीतर पायथन मल्टीलाइन स्ट्रिंग्स के लिए उचित इंडेंटेशन क्या है?

    def method():
        string = """line one
line two
line three"""

या

    def method():
        string = """line one
        line two
        line three"""

या कुछ और?

यह पहले उदाहरण में फ़ंक्शन के बाहर लटके हुए अजीब तरह का दिखता है।


4
Docstrings को विशेष रूप से व्यवहार किया जाता है : पहली पंक्ति के किसी भी इंडेंट को हटा दिया जाता है; अन्य सभी गैर-रिक्त लाइनों पर लिया गया सबसे छोटा सामान्य इंडेंट उन सभी को हटा दिया जाता है। इसके अलावा, पायथन में मल्टीलाइन स्ट्रिंग लिटरल्स दुर्भाग्य से व्हाट्स-व्हाट्सएप के संदर्भ में आप-के-आप-के-आप-के-मिलते हैं: स्ट्रिंग के बीच के सभी वर्ण स्ट्रिंग का हिस्सा बन जाते हैं, जिसमें इंडेंटेशन सहित पायथन रीडिंग इंस्टिंक्ट शामिल हैं, ऐसा लगता है कि इसे उस रेखा के इंडेंट से मापा जाना चाहिए जहां शाब्दिक शुरुआत होती है।
एवगेनी सर्गेव

@EvgeniSergeev प्रसंस्करण उपकरण इस कार्य को करता है (और यह काफी हद तक प्रसंस्करण उपकरण की आपकी पसंद पर निर्भर करता है)। method.__doc__अजगर द्वारा किसी भी अन्य strशाब्दिक की तुलना में किसी भी अधिक संशोधित नहीं है ।
cz

जवाबों:


453

आप शायद साथ लाइन अप करना चाहते हैं """

def foo():
    string = """line one
             line two
             line three"""

चूँकि स्ट्रिंग में नए सिरे और रिक्त स्थान शामिल किए गए हैं, इसलिए आपको इसे स्थगित करना होगा। यदि आप ऐसा नहीं करना चाहते हैं और आपके पास बहुत सारे पाठ हैं, तो आप इसे अलग से एक पाठ फ़ाइल में संग्रहीत करना चाह सकते हैं। यदि एक पाठ फ़ाइल आपके आवेदन के लिए अच्छी तरह से काम नहीं करती है और आप पोस्टप्रोसेस नहीं करना चाहते हैं, तो मैं शायद साथ जाऊंगा

def foo():
    string = ("this is an "
              "implicitly joined "
              "string")

यदि आप उन भागों को ट्रिम करने के लिए मल्टीलाइन स्ट्रिंग पोस्टप्रोसेस करना चाहते हैं जिनकी आपको आवश्यकता नहीं है, तो आपको पीईपी 257textwrap में प्रस्तुत पोस्टप्रोसेसिंग डॉकस्ट्रिंग्स के लिए मॉड्यूल या तकनीक पर विचार करना चाहिए :

def trim(docstring):
    if not docstring:
        return ''
    # Convert tabs to spaces (following the normal Python rules)
    # and split into a list of lines:
    lines = docstring.expandtabs().splitlines()
    # Determine minimum indentation (first line doesn't count):
    indent = sys.maxint
    for line in lines[1:]:
        stripped = line.lstrip()
        if stripped:
            indent = min(indent, len(line) - len(stripped))
    # Remove indentation (first line is special):
    trimmed = [lines[0].strip()]
    if indent < sys.maxint:
        for line in lines[1:]:
            trimmed.append(line[indent:].rstrip())
    # Strip off trailing and leading blank lines:
    while trimmed and not trimmed[-1]:
        trimmed.pop()
    while trimmed and not trimmed[0]:
        trimmed.pop(0)
    # Return a single string:
    return '\n'.join(trimmed)

10
यह लाइन की निरंतरता की 'हैंगिंग इंडेंट' शैली है। यह पीईपी 8 में फ़ंक्शन परिभाषाओं और लंबे बयानों जैसे उद्देश्यों के लिए निर्धारित किया गया है, हालांकि बहुस्तरीय स्ट्रिंग्स के लिए उल्लेख नहीं किया गया है। व्यक्तिगत रूप से यह एक जगह है जिसे मैं PEP8 का पालन करने से मना करता हूं (और इसके बजाय 4-स्पेस इंडेंटिंग का उपयोग करता हूं), क्योंकि मैं दृढ़ता से हैंगिंग इंडेंट को नापसंद करता हूं, जो मेरे लिए कार्यक्रम की उचित संरचना को अस्पष्ट करता है।
बोबिन्स

2
@ बफ़र, आधिकारिक ट्यूटोरियल के 3.1.2 में ("एक दूसरे के बगल में दो स्ट्रिंग लिटरल्स स्वचालित रूप से संक्षिप्त किए गए हैं ...") और भाषा संदर्भ में।
माइक ग्राहम

5
ऑटोमैटिक स्ट्रिंग कॉन्फैटेनेशन के साथ दूसरा रूप न्यूलाइन इट्स एक फीचर शामिल नहीं करता है
माइक ग्राहम

19
trim()के रूप में PEP257 में निर्दिष्ट समारोह के रूप में मानक पुस्तकालय में कार्यान्वित किया जाता है inspect.cleandoc

2
+1 से @bobince की टिप्पणी "लटकते हुए इंडेंट" को यहां अस्वीकार करने के बारे में ... विशेष रूप से क्योंकि यदि आप किसी भिन्न लंबाई के चर नाम stringको textया कुछ भी बदलते हैं , तो अब आपको वस्तुतः हर एक पंक्ति के इंडेंटेशन को अपडेट करने की आवश्यकता है। मल्टीलाइन स्ट्रिंग बस इसे """ठीक से मिलाने के लिए । संकेत रणनीति भविष्य के रिफ्लेक्टर / रखरखाव को जटिल नहीं करना चाहिए, और यह उन स्थानों में से एक है जो पीईपी वास्तव में विफल रहता है
केवलर

255

textwrap.dedentसमारोह से एक के साथ शुरू करने के लिए अनुमति देता है स्रोत में सही खरोज , और फिर उपयोग करने से पहले पाठ से यह पट्टी।

ट्रेड-ऑफ, जैसा कि कुछ अन्य लोगों ने नोट किया है, यह शाब्दिक पर एक अतिरिक्त फ़ंक्शन कॉल है; इस बात को ध्यान में रखते हुए कि ये कोड आपके कोड में कहाँ रखें।

import textwrap

def frobnicate(param):
    """ Frobnicate the scrognate param.

        The Weebly-Ruckford algorithm is employed to frobnicate
        the scrognate to within an inch of its life.

        """
    prepare_the_comfy_chair(param)
    log_message = textwrap.dedent("""\
            Prepare to frobnicate:
            Here it comes...
                Any moment now.
            And: Frobnicate!""")
    weebly(param, log_message)
    ruckford(param)

\लॉग संदेश शाब्दिक में अनुगामी यह सुनिश्चित करना है कि शाब्दिक में लाइन ब्रेक नहीं है; इस तरह, शाब्दिक एक रिक्त रेखा से शुरू नहीं होता है, और इसके बजाय अगली पूर्ण रेखा से शुरू होता है।

से वापसी मान textwrap.dedentइनपुट स्ट्रिंग के प्रत्येक लाइन पर हटाए गए सभी सामान्य प्रमुख व्हाट्सएप इंडेंटेशन के साथ है । तो उपरोक्त log_messageमूल्य होगा:

Prepare to frobnicate:
Here it comes...
    Any moment now.
And: Frobnicate!

2
हालांकि यह एक उचित समाधान है और यह जानकर अच्छा लगता है कि बार-बार फंक्शन के अंदर ऐसा कुछ करना आपदा साबित हो सकता है।
haridsv

@haridsv यह एक आपदा क्यों होगी?
jtmoulia

10
@jtmoulia: आपदा से बेहतर विवरण "अक्षम" होगा क्योंकि textwrap.dedent()कॉल का परिणाम उसके इनपुट तर्क की तरह एक निरंतर मूल्य है।
मार्टिन

2
@haridsv कि आपदा / अक्षमता का मूल है definining एक निरंतर स्ट्रिंग के अंदर एक अक्सर कहा जाता है समारोह। प्रति-कॉल लुकअप के लिए प्रति-कॉल निरंतर परिभाषा का व्यापार करना संभव है। इस तरह से केवल एक ही बार प्रीप्रोसेसिंग चलेगी । एक प्रासंगिक सवाल हो सकता है stackoverflow.com/q/15495376/611007 यह प्रत्येक कॉल के लिए निरंतर परिभाषित करने से बचने के लिए विचारों को सूचीबद्ध करता है। यद्यपि विकल्प के लिए एक खोज की आवश्यकता होती है। फिर भी, इसे स्टोर करने के लिए अनुकूल जगह खोजने के विभिन्न तरीकों का प्रयास किया जाता है। उदाहरण के लिए: फिर अगली पंक्ति । def foo: return foo.xfoo.x = textwrap.dedent("bar")
n611x007

1
मुझे लगता है कि यह अक्षम होगा यदि स्ट्रिंग लॉगिंग के लिए अभिप्रेत है जो केवल डिबग मोड में सक्षम है, और अन्यथा अप्रयुक्त हो जाता है। लेकिन फिर भी एक बहुस्तरीय स्ट्रिंग शाब्दिक लॉग क्यों? इसलिए वास्तविक जीवन का उदाहरण खोजना कठिन है, जहां उपरोक्त अक्षम होगा (यानी जहां यह कार्यक्रम को काफी धीमा कर देता है), क्योंकि जो भी इन तारों का उपभोग कर रहा है वह धीमा होने वाला है।
इवगेनी सर्गेव

53

ऐसे उपयोग करें inspect.cleandoc:

def method():
    string = inspect.cleandoc("""
        line one
        line two
        line three""")

उम्मीद के मुताबिक रिश्तेदार इंडेंटेशन बनाए रखा जाएगा। नीचे टिप्पणी के रूप में , यदि आप खाली लाइनों से पहले रखना चाहते हैं, का उपयोग करें textwrap.dedent। हालाँकि यह पहली पंक्ति को भी तोड़ता है।

नोट: यह संरचना को स्पष्ट करने के लिए संबंधित संदर्भ के तहत कोड के तार्किक ब्लॉकों को इंडेंट करने के लिए अच्छा अभ्यास है। उदाहरण चर से संबंधित बहु-पंक्ति स्ट्रिंग string


5
इतना उलझा हुआ कि यह उत्तर अब तक मौजूद क्यों नहीं था, पाइथन 2.6 केinspect.cleandoc बाद से अस्तित्व में है , जो 2008 था ..? बिल्कुल साफ जवाब, विशेष रूप से क्योंकि यह फांसी इंडेंट शैली का उपयोग नहीं करता है, जो सिर्फ अंतरिक्ष की एक अनावश्यक राशि बर्बाद करता है
केवलर

1
यह समाधान रिक्त पाठ की पहली कुछ पंक्तियों को हटा देता है (यदि कोई हो)। यदि आप वह व्यवहार नहीं चाहते हैं, तो textwrap.dedent docs.python.org/2/library/textwrap.html#textwrap.dedent
joshuakcockrell

1
यह पूर्ण है!
zzzz zzzz

23

एक विकल्प जो अन्य उत्तरों से गायब लगता है (केवल एक टिप्पणी द्वारा नीचे उल्लेख गहरा है) नक्सा निम्नलिखित है:

def foo():
    string = ("line one\n"          # Add \n in the string
              "line two"  "\n"      # Add "\n" after the string
              "line three\n")

यह उचित संरेखण की अनुमति देगा, अंतर्निहित रूप से लाइनों में शामिल हों, और अभी भी लाइन शिफ्ट रखें, जो मेरे लिए, एक कारण है कि मैं वैसे भी मल्टीलाइन स्ट्रिंग्स का उपयोग करना चाहूंगा।

इसके लिए किसी भी पोस्टप्रोसेसिंग की आवश्यकता नहीं है, लेकिन आपको \nकिसी भी जगह पर मैन्युअल रूप से जोड़ने की आवश्यकता है जिसे आप लाइन को समाप्त करना चाहते हैं। या तो इनलाइन या उसके बाद एक अलग स्ट्रिंग के रूप में। उत्तरार्द्ध में कॉपी-पेस्ट करना आसान है।


ध्यान दें कि यह एक अंतर्निहित सम्मिलित स्ट्रिंग का एक उदाहरण है, न कि एक बहुस्तरीय स्ट्रिंग।
ट्रिक

@trk, यह इस अर्थ में बहुस्तरीय है कि स्ट्रिंग में newlines (उर्फ कई लाइनें) शामिल हैं, लेकिन हाँ यह ओपी के प्रारूपण मुद्दों को दरकिनार करने के लिए जुड़ने का उपयोग करता है।
होलीरो

17

कुछ और विकल्प। पायलैब सक्षम के साथ इफिथॉन में, नामस्थान में समर्पण पहले से ही है। मैंने जाँच की और यह matplotlib से है। या इसके साथ आयात किया जा सकता है:

from matplotlib.cbook import dedent

दस्तावेज़ीकरण में यह कहा गया है कि यह टेक्स्टव्रेप के बराबर तेज़ है और ipython में मेरे परीक्षणों में यह वास्तव में मेरे त्वरित परीक्षणों के साथ औसतन 3 गुना तेज है। इसका यह भी लाभ है कि यह किसी भी प्रमुख रिक्त लाइनों को डिस्क्राइब करता है, जिससे आप स्ट्रिंग के निर्माण में लचीले हो सकते हैं:

"""
line 1 of string
line 2 of string
"""

"""\
line 1 of string
line 2 of string
"""

"""line 1 of string
line 2 of string
"""

इन तीन उदाहरणों पर मात्प्लोटलिब का उपयोग करने से समान समझदार परिणाम मिलेगा। Textwrap समर्पण फ़ंक्शन में 1 उदाहरण के साथ एक अग्रणी रिक्त पंक्ति होगी।

स्पष्ट नुकसान यह है कि textwrap मानक पुस्तकालय में है, जबकि matplotlib बाहरी मॉड्यूल है।

कुछ ट्रेडऑफ़ यहाँ ... समर्पण फ़ंक्शंस आपके कोड को अधिक पठनीय बनाते हैं जहाँ स्ट्रिंग्स को परिभाषित किया जाता है, लेकिन बाद में प्रयोग करने योग्य प्रारूप में स्ट्रिंग प्राप्त करने के लिए प्रोसेसिंग की आवश्यकता होती है। डॉकस्ट्रिंग्स में यह स्पष्ट है कि आपको सही इंडेंटेशन का उपयोग करना चाहिए क्योंकि डॉकस्ट्रिंग के अधिकांश उपयोग आवश्यक प्रसंस्करण करेंगे।

जब मुझे अपने कोड में एक गैर लंबी स्ट्रिंग की आवश्यकता होती है, तो मुझे निम्नलिखित कोडली बदसूरत कोड मिलते हैं जहां मैं लंबे स्ट्रिंग को संलग्नक इंडेंटेशन से बाहर जाने देता हूं। निश्चित रूप से "सुंदर बदसूरत से बेहतर है।" पर निश्चित रूप से विफल रहता है, लेकिन कोई यह तर्क दे सकता है कि यह सरल और समर्पण विकल्प की तुलना में अधिक स्पष्ट है।

def example():
    long_string = '''\
Lorem ipsum dolor sit amet, consectetur adipisicing
elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua. Ut enim ad minim veniam, quis
nostrud exercitation ullamco laboris nisi ut aliquip.\
'''
    return long_string

print example()

6

यदि आप एक त्वरित और आसान समाधान चाहते हैं और अपने आप को नए प्रकारों को टाइप करने से बचाते हैं, तो आप इसके बजाय एक सूची का विकल्प चुन सकते हैं:

def func(*args, **kwargs):
    string = '\n'.join([
        'first line of very long string and',
        'second line of the same long thing and',
        'third line of ...',
        'and so on...',
        ])
    print(string)
    return

हालांकि यह सबसे अच्छा तरीका नहीं है, मैंने समय-समय पर इसका इस्तेमाल किया है। यदि आप इसका उपयोग करते हैं, तो आपको सूची के बजाय टपल का उपयोग करना चाहिए, क्योंकि इसमें शामिल होने से पहले इसे संशोधित नहीं किया जाएगा।
लिंडसी साइमन

4

मैं पसंद करता हूं

    def method():
        string = \
"""\
line one
line two
line three\
"""

या

    def method():
        string = """\
line one
line two
line three\
"""

1
यह प्रश्न का उत्तर नहीं देता है, क्योंकि यह प्रश्न स्पष्ट रूप से बताता है कि इंडेंटेशन (फ़ंक्शन के भीतर) मायने रखता है।
bignose

@bignose प्रश्न में कहा गया है "यह अजीब तरह का दिखता है" उपयोग करने के लिए अस्वीकृत नहीं है।
lk_vc

मैं बदसूरत इंडेंटेशन के बिना इसे कैसे पूरा करूंगा?
lfender6445

@ lfender6445 अच्छी तरह से, शायद आप इन सभी तारों को अन्य कोड से एक अलग फ़ाइल में
रख सकते हैं

3

मेरे दो सेंट, इंडेंट पाने के लिए लाइन के अंत से बच जाएं:

def foo():
    return "{}\n"\
           "freq: {}\n"\
           "temp: {}\n".format( time, freq, temp )

1

मैं यहाँ मुद्रण के लिए डॉकस्ट्रिंग के पहचान स्तर को हटाने / सुधारने के लिए एक साधारण 1-लाइनर की तलाश में आया था , बिना इसे अचूक लगे , उदाहरण के लिए इसे स्क्रिप्ट के भीतर "फ़ंक्शन के बाहर लटका" बनाकर।

यहाँ मैं क्या कर रहा हूँ समाप्त:

import string
def myfunction():

    """
    line 1 of docstring
    line 2 of docstring
    line 3 of docstring"""

print str(string.replace(myfunction.__doc__,'\n\t','\n'))[1:] 

जाहिर है, यदि आप टैब कुंजी के बजाय रिक्त स्थान (जैसे 4) के साथ इंडेंट कर रहे हैं तो इसके बजाय कुछ इस तरह का उपयोग करें:

print str(string.replace(myfunction.__doc__,'\n    ','\n'))[1:]

और आपको पहले चरित्र को निकालने की ज़रूरत नहीं है यदि आप इसके बजाय इस तरह दिखना चाहते हैं:

    """line 1 of docstring
    line 2 of docstring
    line 3 of docstring"""

print string.replace(myfunction.__doc__,'\n\t','\n') 

यह क्लास के तरीकों और नेस्टेड क्लासेस पर फेल हो जाता है।
ताकसवेल

1

पहला विकल्प अच्छा है - जिसमें इंडेंटेशन शामिल है। यह अजगर शैली में है - कोड के लिए पठनीयता प्रदान करता है।

इसे ठीक से प्रदर्शित करने के लिए:

print string.lstrip()

यह ट्रिपल उद्धरण तार को प्रारूपित करने का सबसे सरल और साफ तरीका लगता है ताकि आपके पास इंडेंटेशन के कारण अतिरिक्त स्थान न हो
टेलर लिस

4
यह केवल मल्टीलाइन स्ट्रिंग की पहली पंक्ति में अग्रणी रिक्त स्थान को हटा देगा। यह निम्न पंक्तियों को स्वरूपित करने में मदद नहीं करता है।
एम। श्लेनकर

0

यह इस बात पर निर्भर करता है कि आप पाठ को कैसे प्रदर्शित करना चाहते हैं। यदि आप चाहते हैं कि यह सब बाएं-संरेखित किया जाए, तो या तो इसे पहले स्निपेट में रूपांतरित करें या सभी स्थानों को बाईं-ट्रिमिंग करके लाइनों के माध्यम से पुनरावृत्त करें।


5
जिस तरह से डॉकस्ट्रिंग-प्रोसेसिंग टूल काम करता है वह बाईं ओर के सभी स्थान को हटाने के लिए नहीं है , बल्कि पहले इंडेंटेड लाइन जितना है । यह रणनीति थोड़ी अधिक परिष्कृत है और आपके लिए इंडेंट करने की अनुमति देती है और पोस्टप्रोसेड स्ट्रिंग में इसका सम्मान किया जाता है।
माइक ग्राहम

0

स्ट्रिंग के लिए आप स्ट्रिंग को प्रोसेस करने के बाद कर सकते हैं। Docstrings के लिए आपको इसके बजाय फ़ंक्शन को संसाधित करने की आवश्यकता होती है। यहाँ दोनों के लिए एक समाधान है जो अभी भी पठनीय है।

class Lstrip(object):
    def __rsub__(self, other):
        import re
        return re.sub('^\n', '', re.sub('\n$', '', re.sub('\n\s+', '\n', other)))

msg = '''
      Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
      tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
      veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
      commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
      velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
      cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
      est laborum.
      ''' - Lstrip()

print msg

def lstrip_docstring(func):
    func.__doc__ = func.__doc__ - Lstrip()
    return func

@lstrip_docstring
def foo():
    '''
    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
    tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
    veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
    commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
    velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
    cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
    est laborum.
    '''
    pass


print foo.__doc__

1
पीईसी 257 में वर्णित के अनुसार प्रसंस्करण डॉकस्ट्रिंग्स को पहले से ही लगातार खरोज की प्रक्रिया करनी चाहिए । पहले से ही उपकरण हैं - उदाहरण inspect.cleandoc- जो इसे सही तरीके से करते हैं।

0

मैं एक समान मुद्दा रहा हूं, कोड वास्तव में मल्टीलाइन का उपयोग करके अपठनीय हो गया है, मैं कुछ इस तरह से आया हूं

print("""aaaa
"""   """bbb
""")

हाँ, शुरुआत में भयानक लग सकता है लेकिन एम्बेडेड सिंटैक्स काफी जटिल था और अंत में कुछ जोड़ना (जैसे '\ n "') एक समाधान नहीं था


0

आप इस फ़ंक्शन का उपयोग कर सकते हैं trim_indent

import re


def trim_indent(s: str):
    s = re.sub(r'^\n+', '', s)
    s = re.sub(r'\n+$', '', s)
    spaces = re.findall(r'^ +', s, flags=re.MULTILINE)
    if len(spaces) > 0 and len(re.findall(r'^[^\s]', s, flags=re.MULTILINE)) == 0:
        s = re.sub(r'^%s' % (min(spaces)), '', s, flags=re.MULTILINE)
    return s


print(trim_indent("""


        line one
            line two
                line three
            line two
        line one


"""))

परिणाम:

"""
line one
    line two
        line three
    line two
line one
"""
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.