Python çok satırlı dizeleri için uygun girinti


456

Bir işlev içindeki Python çok satırlı dizeleri için uygun girinti nedir?

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

veya

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

veya başka bir şey?

İlk örnekte dizenin fonksiyonun dışında asılı kalması garip görünüyor.


4
Docstring'ler özel olarak ele alınır : ilk satırın tüm girintileri kaldırılır; diğer tüm boş olmayan satırlar üzerinden alınan en küçük ortak girinti bunların hepsinden kaldırılır. Bunun dışında, Python'daki çok satırlı dize değişmezleri, maalesef boşluk olarak ne görüyorsanız onu alırsınız: dize sınırlayıcıları arasındaki tüm karakterler, Python okuma içgüdülerinde, değişmezin başladığı satırın girintisinden ölçülmelidir.
Evgeni Sergeev

@EvgeniSergeev İşleme aracı bu görevi gerçekleştirir (ve bu büyük ölçüde işleme aracı seçiminize bağlıdır). method.__doc__Python'un kendisi tarafından diğer hiçbir strdeğişmezden daha fazla değiştirilmez .
cz

Yanıtlar:


453

Muhtemelen """

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

Yeni satırlar ve boşluklar dizenin kendisine dahil edildiğinden, son işlemden geçirmeniz gerekir. Bunu yapmak istemiyorsanız ve çok fazla metniniz varsa, bunu bir metin dosyasında ayrı olarak saklamak isteyebilirsiniz. Bir metin dosyası uygulamanız için iyi çalışmıyorsa ve sonradan işlem yapmak istemiyorsanız, muhtemelen

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

İhtiyacınız olmayan parçaları kesmek için çok satırlı bir dize sonrası işlemek istiyorsanız textwrap, PEP 257'de sunulan postprocessing docstrings için modülü veya tekniği göz önünde bulundurmalısınız :

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
Bu 'asılı girinti' çizgi devam tarzıdır. PEP8'de fonksiyon tanımları ve long if ifadeleri gibi amaçlar için reçete edilir, ancak çok satırlı dizelerde belirtilmemiştir. Şahsen bu, PEP8'i takip etmeyi reddettiğim (ve bunun yerine 4 boşluklu girinti kullandığım) bir yer.
bobince

2
@buffer, resmi öğreticinin 3.1.2'sinde ("Yan yana iki dize değişmezi otomatik olarak birleştirilir ...") ve dil başvurusunda.
Mike Graham

5
Otomatik dize birleştirmeli ikinci form, yeni satır içermez Bir özelliktir.
Mike Graham

19
trim()Olarak PEP257 belirtilen fonksiyonu olarak standart kitaplığı uygulanmaktadır inspect.cleandoc.

2
+1'den @bobince'nin "asılı girintileri" reddetme hakkındaki yorumu ... Özellikle değişken adını stringolarak textveya farklı bir uzunluktaki herhangi bir şeyle değiştirirseniz, artık kelimenin tam anlamıyla her satırın girintisini güncellemeniz gerekir . çok satırlı dize sadece """düzgün ile eşleşmesi olsun . Girinti stratejisi gelecekteki refactorları / bakımı karmaşıklaştırmamalıdır ve PEP'in gerçekten başarısız olduğu yerlerden biri
kevlarr

255

textwrap.dedentFonksiyon tek başlamak sağlayan kaynakta doğru girinti ve sonra kullanımdan önce metnin ile ayrılmasına.

Bazıları tarafından belirtildiği gibi değiş tokuş, bunun değişmez bir işlev çağrısı olduğudur; bu değişmezleri kodunuza nereye yerleştireceğinize karar verirken bunu dikkate alın.

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)

Arka \giriş mesajı sabit içinde satır sonu hazır olmadığını sağlamak içindir; bu şekilde değişmez değer boş bir satırla başlamaz ve bunun yerine bir sonraki tam satırla başlar.

Dönüş değeri, textwrap.dedentdizenin her satırında tüm ortak satır boşlukları girintisinin kaldırıldığı giriş dizesidir. Yukarıdaki log_messagedeğer şöyle olacaktır:

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

2
Bu makul bir çözüm ve bilmek güzel olsa da, sıkça adlandırılan bir işlev içinde böyle bir şey yapmak bir felaket olabilir.
haridsv

@haridsv Neden bu bir felaket olsun ki?
jtmoulia

10
@jtmoulia: Felaketten daha iyi bir açıklama "verimsiz" olacaktır, çünkü textwrap.dedent()çağrının sonucu , tıpkı girdi argümanı gibi sabit bir değerdir.
martineau

2
@haridsv o felaket / verimsizlik kökeni olan definining sabit dize içine bir sık aradığınız fonksiyonu. Arama başına arama için arama başına sabit tanımını takas etmek mümkündür. Bu şekilde, alıkoyma ön işleme yalnızca bir kez çalışır . Alakalı bir soru stackoverflow.com/q/15495376/611007 olabilir Her aramada sabitin tanımlanmasını önlemek için fikirler listelenir. Alternatifler bir arama gerektirse de. Yine de, saklamak için uygun yeri bulmak için çeşitli yollar denenir. Örneğin: bir def foo: return foo.xsonraki satır foo.x = textwrap.dedent("bar").
n611x007

1
Eğer dize sadece hata ayıklama modunda etkinleştirilen ve aksi takdirde kullanılmayan gidiyor günlük için amaçlanmışsa sanırım verimsiz olurdu. Ama neden yine de çok satırlı bir dizgi değişmezini günlüğe kaydetmelisiniz? Bu yüzden, yukarıdakilerin verimsiz olacağı (yani programı önemli ölçüde yavaşlattığı) gerçek hayattan bir örnek bulmak zordur, çünkü bu dizeleri tüketen her şey daha yavaş olacaktır.
Evgeni Sergeev

53

Şöyle kullanın inspect.cleandoc:

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

Göreceli girinti beklendiği gibi korunacaktır. Gibi yorumladı , kullandığınız boş satırları önceki tutmak istiyorsanız, aşağıda textwrap.dedent. Ancak bu ilk satır sonunu da korur.

Not: Yapıyı netleştirmek için mantıksal kod bloklarını ilgili bağlamı altında girintili yapmak iyi bir uygulamadır. Örneğin, değişkene ait çok satırlı dize string.


5
Öyle ki, bu cevabın neden inspect.cleandocşimdiye kadar var olmadığını , 2008 Python 2.6'dan bu yana varlığını karıştırdınız . Kesinlikle en temiz cevap, özellikle de gereksiz miktarda alan
kaybeden

1
Bu çözüm, boş metin satırlarının ilk birkaç satırını (varsa) kaldırır. Bu davranışı istemiyorsanız, textwrap.dedent docs.python.org/2/library/textwrap.html#textwrap.dedent
joshuakcockrell

1
Bu harika!
zzzz zzzz

23

Diğer cevaplardan eksik gibi görünen bir seçenek (sadece naxa tarafından yapılan bir açıklamada derinden bahsedilmiştir):

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

Bu, düzgün hizalamaya izin verir, çizgilere dolaylı olarak katılır ve yine de benim için çok satırlı dizeleri kullanmak istemem nedenlerinden biri olan satır kaymasını koruyacaktır.

Herhangi bir postprocess gerektirmez, ancak \nsatırın bitmesini istediğiniz herhangi bir yere manuel olarak eklemeniz gerekir . Satır içi veya sonrasında ayrı bir dize olarak. İkincisinin kopyalayıp yapıştırması daha kolaydır.


Bunun, çok satırlı bir dize değil, örtük olarak birleştirilen bir dize örneği olduğunu unutmayın.
trk

@trk, dizenin yeni satırlar (birden çok satır olarak da bilinir) içermesi anlamında çok satırlıdır, ancak evet OP'nin biçimlendirme sorunlarını aşmak için birleşmeyi kullanır.
holroy

17

Biraz daha seçenek. Pylab'ın etkin olduğu Ipython'da, dedent zaten ad alanında. Kontrol ettim ve matplotlib'den. Veya ile ithal edilebilir:

from matplotlib.cbook import dedent

Dokümantasyonda textwrap eşdeğerinden daha hızlı olduğunu ve ipython'daki testlerimde hızlı testlerimle ortalama 3 kat daha hızlı olduğunu belirtiyor. Ayrıca, dizeyi nasıl oluşturduğunuzda esnek olmanızı sağlayan önde gelen boş satırları atması avantajına sahiptir:

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

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

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

Bu üç örnek üzerinde matplotlib özünün kullanılması aynı mantıklı sonucu verecektir. Textwrap dedent işlevi 1. örnekle önde gelen bir boş satıra sahip olacaktır.

Açık olan dezavantajı, matplotlib harici modül iken textwrap'ın standart kütüphanede olmasıdır.

Buradaki bazı dengesizlikler ... özel işlevler, dizelerin tanımlandığı yerde kodunuzu daha okunabilir hale getirir, ancak dizeyi kullanılabilir biçimde almak için daha sonra işlem yapılmasını gerektirir. Docstring'lerde, docstring'in çoğu kullanımı gerekli işlemi yapacağından doğru girinti kullanmanız gerektiği açıktır.

Kodumda uzun olmayan bir dizeye ihtiyacım olduğunda, uzun dizenin çevreleyen girintiden düşmesine izin verdiğim şu kuşkulu çirkin kodu buluyorum. Kesinlikle "Güzel çirkin daha iyidir." Başarısız, ama bir kişi daha basit ve daha açık seçik olduğu iddia edilebilir.

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

Hızlı ve kolay bir çözüm istiyorsanız ve kendinizi yeni satırlar yazmaktan kurtarırsanız, bunun yerine bir listeyi tercih edebilirsiniz, örneğin:

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

Bu en iyi yaklaşım olmasa da, zaman zaman kullandım. Eğer varsa bunu kullanmak o birleştirilmeden önce değiştirilecek gitmiyor çünkü, sen bir liste yerine bir demet kullanmalıdır.
Lyndsy Simon

4

tercih ederim

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

veya

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

1
Bu soruya cevap vermez, çünkü soru açıkça (işlev içindeki) girintinin önemli olduğunu belirtir.
bignose

@bignose Soru kullanımına izin verilmeyen "Tuhaf görünüyor" dedi.
lk_vc

bunu çirkin girinti olmadan nasıl başarabilirim?
lfender6445

@ lfender6445 iyi, belki tüm bu dizeleri diğer kodlardan ayrı bir dosyaya yerleştirebilirsiniz ...
lk_vc

3

İki sentim, girintileri almak için satır sonundan kaç:

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

1

Buraya , örneğin komut dosyasında "fonksiyonun dışına as" yaparak, düzensiz görünmeden , yazdırmak için doktrin kimlik seviyesini kaldırmak / düzeltmek için basit bir 1-astar aramaya geldim .

İşte yaptığım şey:

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:] 

Tabii ki, sekme tuşu yerine boşluklarla (örneğin 4) girintiliyorsanız, bunun gibi bir şey kullanın:

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

Ve doktor dizelerinizin bunun gibi görünmesini isterseniz ilk karakteri kaldırmanız gerekmez:

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

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

Bu sınıf yöntemleri ve iç içe sınıflar başarısız olur.
tacaswell

1

İlk seçenek iyi olanıdır - girinti dahil. Python tarzındadır - kod için okunabilirlik sağlar.

Düzgün görüntülemek için:

print string.lstrip()

Bu, üçlü alıntı dizelerini biçimlendirmenin en basit ve en temiz yolu gibi görünüyor, böylece girinti nedeniyle fazladan boşluklara sahip değilsiniz
Taylor Liss

4
Bu, yalnızca çok satırlı dizenin ilk satırındaki önde gelen boşlukları siler. Aşağıdaki satırları biçimlendirmeye yardımcı olmaz.
M. Schlenker

0

Metnin nasıl görüntülenmesini istediğinize bağlıdır. Hepsinin sola hizalanmasını istiyorsanız, ya ilk snippet'te olduğu gibi biçimlendirin ya da tüm alanı sola kesen satırlar boyunca yineleyin.


5
Öğretme-işleme araçlarının çalışma şekli soldaki tüm alanı değil , ilk girintili çizgi kadar kaldırmaktır . Bu strateji biraz daha karmaşıktır ve sonradan işlenmiş dizede girintilemenizi ve ona saygı duymanızı sağlar.
Mike Graham

0

Dizeler için dizeyi işledikten hemen sonra yapabilirsiniz. Docstrings için bunun yerine işlevi işlemeniz gerekir. İşte hala okunabilir her ikisi için bir çözüm.

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
İşleme dizeleri PEP 257'de açıklandığı gibi tutarlı girinti işlemek zorundadır . Bunu inspect.cleandocdoğru şekilde yapan araçlar var - örneğin .
bignose

0

Benzer bir sorun yaşıyorum, kod çok satırlı kullanarak gerçekten okunamıyor, ben gibi bir şey ile çıktı

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

evet, başlangıçta korkunç görünebilirdi, ancak gömülü sözdizimi oldukça karmaşıktı ve sonuna bir şey eklemek ('\ n "' gibi) bir çözüm değildi


0

Bu işlevi trim_indent işlevini kullanabilirsiniz .

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


"""))

Sonuç:

"""
line one
    line two
        line three
    line two
line one
"""
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.