क्या पायथन कोड की एक पंक्ति इसके इंडेंटेशन नेस्टिंग स्तर को जान सकती है?


149

कुछ इस तरह से:

print(get_indentation_level())

    print(get_indentation_level())

        print(get_indentation_level())

मैं कुछ इस तरह से प्राप्त करना चाहूंगा:

1
2
3

क्या कोड को इस तरह से पढ़ा जा सकता है?

मैं चाहता हूं कि कोड के अधिक नेस्टेड भागों से आउटपुट अधिक नेस्टेड हो। इसी तरह से यह कोड को पढ़ना आसान बनाता है, यह आउटपुट को पढ़ने में आसान बनाता है।

बेशक मैं इस मैन्युअल रूप से लागू कर सकता है, जैसे का उपयोग कर .format(), लेकिन मैं क्या मन में था एक कस्टम प्रिंट समारोह जो होता था print(i*' ' + string)जहां iखरोज स्तर है। यह मेरे टर्मिनल पर पठनीय आउटपुट बनाने का एक त्वरित तरीका होगा।

क्या ऐसा करने का एक बेहतर तरीका है जो श्रमसाध्य मैनुअल प्रारूपण से बचा जाता है?


70
मैं वास्तव में उत्सुक हूं कि आपको इसकी आवश्यकता क्यों है।
हैरिसन

12
@ मुझे अपने कोड के आउटपुट को इंडेंट करना था कि यह कोड में कैसे इंडेंट किया गया था।
फैब वॉन बेलिंग्सहॉसेन

14
असली सवाल यह है: आपको इसकी आवश्यकता क्यों होगी? इंडेंटेशन स्तर स्थिर है; जब आप get_indentation_level()अपने कोड में स्टेटमेंट डालते हैं तो आप इसे निश्चितता के साथ जानते हैं । आप बस के रूप में अच्छी तरह से print(3)या सीधे कुछ भी कर सकते हैं। क्या हो सकता है अधिक पुनरावृत्ति फ़ंक्शन कॉल स्टैक पर नेस्टिंग का वर्तमान स्तर है।
Tobias_k

19
क्या यह आपके कोड को डीबग करने के उद्देश्य से है? यह या तो निष्पादन के प्रवाह को लॉग करने का एक सुपर प्रतिभाशाली तरीका है या एक साधारण समस्या के लिए एक सुपर ओवर-इंजीनियर समाधान की तरह है, और मुझे यकीन नहीं है कि यह कौन है ... शायद दोनों!
ब्लैकहॉक

7
@ फैबोनबेलिंग्सज़ोन: ऐसा लगता है कि आप उम्मीद से बहुत कम पठनीय होंगे। मुझे लगता है कि आप बेहतर तरीके से एक depthपैरामीटर के आसपास से गुजरते हुए और अन्य कार्यों के लिए इसे पारित करते समय आवश्यक मान जोड़ सकते हैं। आपके कोड का घोंसला आपके उत्पादन से इच्छित इंडेंटेशन के लिए साफ तौर पर मेल करने की संभावना नहीं है।
user2357112

जवाबों:


115

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

if True:
    print(
get_nesting_level())

कॉल को get_nesting_levelवास्तव में एक स्तर गहरा घोंसला है, इस तथ्य के बावजूद कि get_nesting_levelकॉल की लाइन पर कोई प्रमुख व्हाट्सएप नहीं है । इस बीच, निम्नलिखित कोड में:

print(1,
      2,
      get_nesting_level())

get_nesting_levelइसकी लाइन पर प्रमुख व्हाट्सएप की मौजूदगी के बावजूद कॉल को शून्य स्तर तक नेस्टेड किया जाता है।

निम्नलिखित कोड में:

if True:
  if True:
    print(get_nesting_level())

if True:
    print(get_nesting_level())

दो कॉल get_nesting_levelअलग-अलग घोंसले के स्तर पर हैं, इस तथ्य के बावजूद कि प्रमुख व्हाट्सएप समान है।

निम्नलिखित कोड में:

if True: print(get_nesting_level())

क्या यह शून्य स्तर, या एक है? औपचारिक व्याकरण में INDENTऔर DEDENTटोकन के संदर्भ में , यह शून्य स्तर गहरा है, लेकिन आप उसी तरह महसूस नहीं कर सकते हैं।


यदि आप ऐसा करना चाहते हैं, तो आपको कॉल और गिनती INDENTऔर DEDENTटोकन के बिंदु तक पूरी फ़ाइल को टोकन देना होगा। इस tokenizeतरह के एक समारोह के लिए मॉड्यूल बहुत उपयोगी होगा:

import inspect
import tokenize

def get_nesting_level():
    caller_frame = inspect.currentframe().f_back
    filename, caller_lineno, _, _, _ = inspect.getframeinfo(caller_frame)
    with open(filename) as f:
        indentation_level = 0
        for token_record in tokenize.generate_tokens(f.readline):
            token_type, _, (token_lineno, _), _, _ = token_record
            if token_lineno > caller_lineno:
                break
            elif token_type == tokenize.INDENT:
                indentation_level += 1
            elif token_type == tokenize.DEDENT:
                indentation_level -= 1
        return indentation_level

2
यह उस तरह से काम नहीं करता है जब मुझे लगता है कि जब get_nesting_level()उस फ़ंक्शन कॉल के भीतर कॉल किया जाता है the यह उस फ़ंक्शन के भीतर नेस्टिंग स्तर देता है। क्या यह 'वैश्विक' घोंसले के शिकार स्तर को लौटाने के लिए फिर से लिखा जा सकता है?
फैब वॉन बेलिंगशॉयन

11
@FabvonBellingshausen: आपको इंडेंटेशन नेस्टिंग लेवल और फंक्शन कॉल नेस्टिंग लेवल मिल सकता है। यह फ़ंक्शन इंडेंटेशन नेस्टिंग स्तर देता है। फंक्शन कॉल नेस्टिंग स्तर बल्कि अलग होगा, और मेरे सभी उदाहरणों के लिए 0 का स्तर देगा। आप खरोज का एक तरह चाहते हैं / नेस्टिंग स्तर संकर फोन है कि दोनों समारोह कॉल और नियंत्रण प्रवाह संरचनाओं की तरह के लिए वेतन वृद्धि whileऔर with, कि साध्य होगा, लेकिन यह आप के लिए क्या नहीं कहा है, और प्रश्न बदलते इस बिंदु पर कुछ अलग पूछने के लिए एक बुरा विचार होगा।
user2357112

38
संयोग से, मैं सभी लोगों के साथ पूर्ण रूप से समझौता कर रहा हूं कि यह वास्तव में अजीब बात है। आप जिस भी समस्या को हल करने का प्रयास कर रहे हैं, उसे हल करने के लिए शायद एक बेहतर तरीका है, और इस पर भरोसा करने से आपको अपने इंडेंटेशन या फ़ंक्शन कॉल संरचना को बदलने से बचने के लिए सभी प्रकार के बुरा हैक का उपयोग करने के लिए मजबूर करने की संभावना है। आपके कोड में परिवर्तन।
user2357112

4
मैं निश्चित रूप से उम्मीद नहीं करता था कि किसी ने वास्तव में इसका उत्तर दिया है। ( linecacheइस तरह से सामान के लिए मॉड्यूल पर विचार करें - यह ट्रेसबैक प्रिंट करने के लिए उपयोग किया जाता है, और ज़िप फ़ाइलों और अन्य अजीब आयात चाल से आयातित मॉड्यूल को संभाल सकता है)
Eevee

6
@Eevee: मैं निश्चित रूप से करने के लिए इतने सारे लोग उम्मीद नहीं थी वोट दें इस! linecacheफ़ाइल की मात्रा को कम करने के लिए अच्छा हो सकता है I / O (और इसके बारे में मुझे याद दिलाने के लिए धन्यवाद), लेकिन अगर मैंने इसका अनुकूलन करना शुरू कर दिया है, तो मैं परेशान हो जाऊंगा कि हम किस तरह से अनावश्यक रूप से पुनरावृत्ति के लिए उसी फ़ाइल को फिर से टोकन कर रहे हैं। एक ही कॉल, या एक ही फ़ाइल के भीतर कई कॉल साइटों के लिए। ऐसे कई तरीके हैं जो हम इसे भी अनुकूलित कर सकते हैं, लेकिन मुझे यकीन नहीं है कि मैं वास्तव में इस पागल चीज़ को ट्यून और बुलेटप्रूफ करना चाहता हूं।
user2357112

22

हाँ, यह निश्चित रूप से संभव है, यहाँ एक कार्य उदाहरण है:

import inspect

def get_indentation_level():
    callerframerecord = inspect.stack()[1]
    frame = callerframerecord[0]
    info = inspect.getframeinfo(frame)
    cc = info.code_context[0]
    return len(cc) - len(cc.lstrip())

if 1:
    print get_indentation_level()
    if 1:
        print get_indentation_level()
        if 1:
            print get_indentation_level()

4
@Prune द्वारा की गई टिप्पणी से संबंधित, क्या यह रिक्त स्थान के बजाय स्तरों में इंडेंटेशन वापस करने के लिए बनाया जा सकता है? क्या हमेशा 4 से भाग देना ठीक रहेगा?
फैब वॉन बेलिंग्सहॉउस

2
नहीं, इंडेंट स्तर प्राप्त करने के लिए 4 से भाग दें इस कोड के साथ काम नहीं करेंगे। अंतिम प्रिंट स्टेटमेंट के इंडेंट के स्तर में वृद्धि करके सत्यापित कर सकते हैं, पिछले मुद्रित मूल्य में वृद्धि हुई है।
क्रेग बर्गलर

10
एक अच्छी शुरुआत, लेकिन वास्तव में इस सवाल का जवाब नहीं है। रिक्त स्थान की संख्या इंडेंटेशन स्तर के समान नहीं है।
विम

1
यह इतना आसान नहीं है। सिंगल स्पेस के साथ 4 स्पेस को बदलने से कोड का लॉजिक बदल सकता है।
विम

1
लेकिन यह कोड ओपी के लिए जो देख रहा था, उसके लिए एकदम सही है: (ओपी टिप्पणी # 9): 'मैं अपने कोड के आउटपुट को इंडेंट करना चाहता था कि यह कोड में कैसे इंडेंट किया गया था।' तो वह कुछ ऐसा कर सकता हैprint('{Space}'*get_indentation_level(), x)
क्रेग बर्गलर

10

आप sys.current_frame.f_linenoलाइन नंबर प्राप्त करने के लिए उपयोग कर सकते हैं । फिर इंडेंटेशन लेवल की संख्या ज्ञात करने के लिए आपको पिछली लाइन को जीरो इंडेंटेशन के साथ ढूंढना होगा, फिर उस लाइन के नंबर से करंट लाइन को घटाएं जिससे आपको इंडेंटेशन का नंबर मिलेगा:

import sys
current_frame = sys._getframe(0)

def get_ind_num():
    with open(__file__) as f:
        lines = f.readlines()
    current_line_no = current_frame.f_lineno
    to_current = lines[:current_line_no]
    previous_zoro_ind = len(to_current) - next(i for i, line in enumerate(to_current[::-1]) if not line[0].isspace())
    return current_line_no - previous_zoro_ind

डेमो:

if True:
    print get_ind_num()
    if True:
        print(get_ind_num())
        if True:
            print(get_ind_num())
            if True: print(get_ind_num())
# Output
1
3
5
6

यदि आप चाहते हैं कि आपके साथ प्रीवीहाउस लाइनों के आधार पर इंडेंटेशन स्तर की संख्या :थोड़े बदलाव के साथ कर सके:

def get_ind_num():
    with open(__file__) as f:
        lines = f.readlines()

    current_line_no = current_frame.f_lineno
    to_current = lines[:current_line_no]
    previous_zoro_ind = len(to_current) - next(i for i, line in enumerate(to_current[::-1]) if not line[0].isspace())
    return sum(1 for line in lines[previous_zoro_ind-1:current_line_no] if line.strip().endswith(':'))

डेमो:

if True:
    print get_ind_num()
    if True:
        print(get_ind_num())
        if True:
            print(get_ind_num())
            if True: print(get_ind_num())
# Output
1
2
3
3

और एक वैकल्पिक उत्तर के रूप में यहां इंडेंटेशन (व्हॉट्सएप) की संख्या प्राप्त करने के लिए एक समारोह है:

import sys
from itertools import takewhile
current_frame = sys._getframe(0)

def get_ind_num():
    with open(__file__) as f:
        lines = f.readlines()
    return sum(1 for _ in takewhile(str.isspace, lines[current_frame.f_lineno - 1]))

प्रश्न इंडेंटेशन स्तरों की संख्या के लिए पूछा गया, न कि रिक्त स्थान की संख्या के लिए। वे जरूरी आनुपातिक नहीं हैं।
विम

2 - - 3 - 3 अपने डेमो कोड के लिए उत्पादन 1 होना चाहिए
क्रेग Burgler

@CraigBurgler 1 - 2 - 3 - 3 प्राप्त करने के लिए हम वर्तमान लाइन से पहले लाइनों की संख्या को गिन सकते हैं जो :तब तक समाप्त होती हैं जब तक कि हम शून्य इंडेंटेशन के साथ लाइन का सामना नहीं करते हैं, संपादित करें देखें!
कासम्रवद

2
हम्म् ... ठीक है ... अब कुछ @ user2357112 के परीक्षण मामलों की कोशिश करें;)
क्रेग बर्गलर

@CraigBurgler यह समाधान ज्यादातर मामलों के लिए है, और उस जवाब के बारे में, यह भी एक सामान्य तरीका है, यह एक व्यापक समाधान भी नहीं देता है। कोशिश{3:4, \n 2:get_ind_num()}
कास्रामव्ड

7

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

#!/usr/bin/env python
# coding: utf8
from __future__ import absolute_import, division, print_function


class IndentedPrinter(object):

    def __init__(self, level=0, indent_with='  '):
        self.level = level
        self.indent_with = indent_with

    def __enter__(self):
        self.level += 1
        return self

    def __exit__(self, *_args):
        self.level -= 1

    def print(self, arg='', *args, **kwargs):
        print(self.indent_with * self.level + str(arg), *args, **kwargs)


def main():
    indented = IndentedPrinter()
    indented.print(indented.level)
    with indented:
        indented.print(indented.level)
        with indented:
            indented.print('Hallo', indented.level)
            with indented:
                indented.print(indented.level)
            indented.print('and back one level', indented.level)


if __name__ == '__main__':
    main()

आउटपुट:

0
  1
    Hallo 2
      3
    and back one level 2

6
>>> import inspect
>>> help(inspect.indentsize)
Help on function indentsize in module inspect:

indentsize(line)
    Return the indent size, in spaces, at the start of a line of text.

12
यह रिक्त स्थान में इंडेंटेशन देता है, स्तरों में नहीं। जब तक प्रोग्रामर लगातार इंडेंटेशन मात्रा का उपयोग नहीं करता है, तब तक यह स्तरों में परिवर्तित होने के लिए बदसूरत हो सकता है।
छँटाई

4
क्या फ़ंक्शन अनिर्दिष्ट है? मैं इसे यहाँ
जिंजप्लस २ find
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.