পাইথনের মুদ্রণ ফাংশনটি "হ্যাক" করা কি সম্ভব?


151

দ্রষ্টব্য: এই প্রশ্নটি কেবল তথ্যগত উদ্দেশ্যে। পাইথনের অভ্যন্তরীণ অঞ্চলে কতটা গভীরভাবে এটি যেতে পারে তা দেখতে আগ্রহী।

খুব বেশি দিন আগে, মুদ্রণ বিবৃতিতে প্রেরণ করা স্ট্রিংগুলি কল করার পরে / চলাকালীন সময়ে সংশোধন করা যায় কিনা তা নিয়ে একটি নির্দিষ্ট প্রশ্নের ভিতরে আলোচনা শুরু printহয়েছিল। উদাহরণস্বরূপ, ফাংশনটি বিবেচনা করুন:

def print_something():
    print('This cat was scared.')

এখন, যখন printচালানো হবে, তারপরে টার্মিনালের আউটপুট প্রদর্শিত হবে:

This dog was scared.

লক্ষ্য করুন "বিড়াল" শব্দটির পরিবর্তে "কুকুর" শব্দটি প্রতিস্থাপিত হয়েছে। কোথাও কোথাও কোনও কিছু ছাপানো হয়েছে তা পরিবর্তনের জন্য সেই অভ্যন্তরীণ বাফারগুলিকে সংশোধন করতে সক্ষম হয়েছিল। ধরে নিন মূল কোড লেখকের সুস্পষ্ট অনুমতি ছাড়াই এটি করা হয়েছে (সুতরাং, হ্যাকিং / হাইজ্যাকিং)।

জ্ঞানী @ বার্নার্টের এই মন্তব্যটি , বিশেষত, আমাকে এই চিন্তাভাবনা করেছে:

এটি করার কয়েকটি উপায় রয়েছে তবে সেগুলি খুব কুরুচিপূর্ণ এবং কখনই করা উচিত নয়। সবচেয়ে কম কুরুচিপূর্ণ উপায় হ'ল codeফাংশনের অভ্যন্তরে থাকা অবজেক্টটিকে একটি ভিন্ন co_consts তালিকার সাথে প্রতিস্থাপন করা । এর পরে সম্ভবত স্ট্রির অভ্যন্তরীণ বাফারটি অ্যাক্সেস করতে সি এপিআইতে পৌঁছে যাচ্ছে। [...]

সুতরাং, দেখে মনে হচ্ছে এটি আসলেই সম্ভব।

এই সমস্যাটি কাছে যাওয়ার আমার নির্বুদ্ধ উপায়টি এখানে:

>>> import inspect
>>> exec(inspect.getsource(print_something).replace('cat', 'dog'))
>>> print_something()
This dog was scared.

অবশ্যই, execখারাপ, কিন্তু এটি সত্যই প্রশ্নের উত্তর দেয় না, কারণ এটি যখন / পরে print বলা হয় তখন কোনও কিছুই সংশোধন করে না ।

@ বার্নার্ট যেভাবে ব্যাখ্যা করেছেন তা এটি কীভাবে হবে?


3
যাইহোক, ints জন্য অভ্যন্তরীণ স্টোরেজ স্ট্রিং তুলনায় অনেক সহজ, এবং আরও ভাসমান। এবং, একটি বোনাস হিসেবে, এটা আরো অনেক সুস্পষ্ট কেন এটি একটি খারাপ ধারণা মান পরিবর্তন করতে 42করতে 23কেন এটি একটি খারাপ ধারণা মান পরিবর্তন করতে চেয়ে "My name is Y"থেকে "My name is X"
14:58 '

উত্তর:


244

প্রথমত, আসলে অনেক কম হ্যাকি উপায় আছে। আমরা যা করতে চাই তা হ'ল কি printপ্রিন্ট পরিবর্তন করা উচিত ?

_print = print
def print(*args, **kw):
    args = (arg.replace('cat', 'dog') if isinstance(arg, str) else arg
            for arg in args)
    _print(*args, **kw)

বা, একইভাবে, আপনি sys.stdoutপরিবর্তে monkeypatch করতে পারেন print


এছাড়াও, exec … getsource …ধারণাটি নিয়ে কোনও ভুল নেই । ঠিক আছে, অবশ্যই এটিতে প্রচুর ভুল আছে , তবে এখানে যা অনুসরণ করা হয় তার চেয়ে কম ...


তবে আপনি যদি ফাংশন অবজেক্টের কোড কনস্ট্যান্টগুলি সংশোধন করতে চান তবে আমরা এটি করতে পারি।

আপনি যদি সত্যিকারের জন্য কোড অবজেক্টের সাথে চারপাশে খেলতে চান তবে আপনার নিজের মতো লাইব্রেরিটি bytecode(এটি শেষ হয়ে গেলে) বা byteplay(ততক্ষণ, অথবা পুরানো পাইথন সংস্করণগুলির জন্য) ম্যানুয়ালি না করে ব্যবহার করা উচিত। এমনকি এই তুচ্ছ জিনিসের জন্যও, CodeTypeআরম্ভকারীটি একটি ব্যথা হয়; আপনার যদি ঠিক করার মতো জিনিসগুলি করার দরকার হয় তবে lnotabকেবল একজন পাগল নিজেই এটি করতে পারে।

এছাড়াও, এটি বলার অপেক্ষা রাখে না যে সমস্ত পাইথন বাস্তবায়ন সিপিথন-স্টাইলের কোড অবজেক্ট ব্যবহার করে না। এই কোডটি সিপথন ৩.7-এ কাজ করবে এবং সম্ভবত কিছু সংখ্যক পরিবর্তন (এবং কোড-হ্যাকিংয়ের জিনিস নয়, তবে জেনারেটর এক্সপ্রেশনগুলির মতো জিনিস) সহ কমপক্ষে ২.২-এর সমস্ত সংস্করণ ফিরে আসবে, তবে এটি আয়রন পাইথনের কোনও সংস্করণে কাজ করবে না।

import types

def print_function():
    print ("This cat was scared.")

def main():
    # A function object is a wrapper around a code object, with
    # a bit of extra stuff like default values and closure cells.
    # See inspect module docs for more details.
    co = print_function.__code__
    # A code object is a wrapper around a string of bytecode, with a
    # whole bunch of extra stuff, including a list of constants used
    # by that bytecode. Again see inspect module docs. Anyway, inside
    # the bytecode for string (which you can read by typing
    # dis.dis(string) in your REPL), there's going to be an
    # instruction like LOAD_CONST 1 to load the string literal onto
    # the stack to pass to the print function, and that works by just
    # reading co.co_consts[1]. So, that's what we want to change.
    consts = tuple(c.replace("cat", "dog") if isinstance(c, str) else c
                   for c in co.co_consts)
    # Unfortunately, code objects are immutable, so we have to create
    # a new one, copying over everything except for co_consts, which
    # we'll replace. And the initializer has a zillion parameters.
    # Try help(types.CodeType) at the REPL to see the whole list.
    co = types.CodeType(
        co.co_argcount, co.co_kwonlyargcount, co.co_nlocals,
        co.co_stacksize, co.co_flags, co.co_code,
        consts, co.co_names, co.co_varnames, co.co_filename,
        co.co_name, co.co_firstlineno, co.co_lnotab,
        co.co_freevars, co.co_cellvars)
    print_function.__code__ = co
    print_function()

main()

কোড অবজেক্টগুলি হ্যাক করে কী ভুল হতে পারে? প্রায়শই কেবল সেগফাল্টস, RuntimeErrorযেগুলি পুরো স্ট্যাক খায়, আরও সাধারণ RuntimeErrorযেগুলি পরিচালনা করা যায়, বা আবর্জনার মানগুলি সম্ভবত একটি TypeErrorবা AttributeErrorযখন আপনি সেগুলি ব্যবহার করার চেষ্টা করবেন তখন উত্থাপন করবে। উদাহরণস্বরূপ, RETURN_VALUEস্ট্যাকের সাথে কিছুই না দিয়ে একটি কোড অবজেক্ট তৈরি করার চেষ্টা করুন ( আগে b'S\0'3.6+ এর জন্য বাইটকোড b'S'), বা বাইটোকোডে co_constsযখন রয়েছে তখন একটি ফাঁকা টিপল LOAD_CONST 0সহ বা varnames1 দ্বারা হ্রাস LOAD_FASTপেয়েছে তাই সর্বোচ্চটি একটি ফ্রিভারকে বোঝায় / সেলভার সেল কিছু বাস্তব মজাদার জন্য, আপনি যদি lnotabভুলটি যথেষ্ট পরিমাণে পান তবে আপনার কোডটি কেবলমাত্র ডিবাগারে চালিত হলে সেগফল্ট হবে।

ব্যবহার bytecodeবা byteplayযারা সমস্যার সমস্ত থেকে আপনাকে রক্ষা করবে না, কিন্তু তারা কিছু মৌলিক মানসিক সুস্থতা চেক, এবং চমৎকার সাহায্যকারী যে আপনার কোড একটি খণ্ড সন্নিবেশ ভালো জিনিস না দেওয়া আছে এবং এটা এত আপনি যা করতে পারেন 'সব অফসেট এবং লেবেল আপডেট সম্পর্কে চিন্তা করা যাক না এটি ভুল না হওয়া, ইত্যাদি। (প্লাস, তারা আপনাকে সেই হাস্যকর 6-লাইনের কনস্ট্রাক্টর টাইপ করা থেকে বিরত রাখে এবং এমনটি করে আসে এমন নির্বোধ টাইপগুলি ডিবাগ করতে বাধ্য করে))


এখন # 2 এ।

আমি উল্লেখ করেছি যে কোড অবজেক্টগুলি অপরিবর্তনীয়। এবং অবশ্যই কনসেটগুলি একটি টিপল, তাই আমরা সরাসরি এটি পরিবর্তন করতে পারি না। এবং কনস্ট টিউপলের জিনিসটি একটি স্ট্রিং, যা আমরা সরাসরিও পরিবর্তন করতে পারি না। এ কারণেই আমাকে একটি নতুন কোড অবজেক্ট তৈরি করতে একটি নতুন টিউপল তৈরি করতে একটি নতুন স্ট্রিং তৈরি করতে হয়েছিল।

কিন্তু আপনি যদি সরাসরি একটি স্ট্রিং পরিবর্তন করতে পারে?

আচ্ছা, প্রচ্ছদের নীচে যথেষ্ট গভীর, সবকিছু কিছু সি-এর ডেটা কেবলমাত্র পয়েন্টার, তাই না? আপনি যদি সিপিথন ব্যবহার করছেন তবে অবজেক্টগুলিকে অ্যাক্সেস করার জন্য একটি সিআই এপিআই রয়েছে এবং আপনি ctypesপাইথনের মধ্যে থেকেই সেই এপিআইটি অ্যাক্সেস করতে ব্যবহার করতে পারেন , এটি এমন এক ভয়ানক ধারণা যে তারা pythonapiস্টাডলিবের ctypesমডিউলে একটি অধিকার রেখেছিল । :) আপনার জানা সবচেয়ে গুরুত্বপূর্ণ কৌশলটি id(x)হ'ল xমেমরির আসল পয়েন্টার (যেমন হিসাবে int) an

দুর্ভাগ্যক্রমে, স্ট্রিংগুলির সিআইপি আমাদের নিরাপদভাবে ইতিমধ্যে হিমায়িত স্ট্রিংয়ের অভ্যন্তরীণ স্টোরেজে যেতে দেয় না। তাই নিরাপদে স্ক্রু করুন, আসুন আমরা কেবল শিরোনাম ফাইলগুলি পড়ি এবং সেই সঞ্চয়টি নিজেরাই সন্ধান করি।

যদি আপনি সিপথন ৩.৪ - ৩.7 ব্যবহার করেন (এটি পুরানো সংস্করণগুলির চেয়ে পৃথক, এবং ভবিষ্যতের জন্য কে জানে), খাঁটি ASCII দ্বারা তৈরি মডিউলটির একটি স্ট্রিং আক্ষরিক সংক্ষিপ্ত ASCII ফর্ম্যাট ব্যবহার করে সংরক্ষণ করা হবে, যার অর্থ কাঠামো তাড়াতাড়ি শেষ হয় এবং এএসসিআইআই বাইটের বাফারটি মেমরির সাথে সাথেই অনুসরণ করে। আপনি যদি স্ট্রিং-এ অ-এএসসিআইআই অক্ষর বা কিছু ধরণের অ-আক্ষরিক স্ট্রিং রাখেন তবে এটি বিভাজিত হবে (সম্ভবত সেগফল্টে) তবে আপনি বিভিন্ন ধরণের স্ট্রিংয়ের জন্য বাফারটি অ্যাক্সেস করার অন্যান্য 4 টি পদ্ধতিতে পড়তে পারেন।

জিনিসগুলিকে কিছুটা সহজ করার জন্য, আমি superhackyinternalsআমার গিটহাব বন্ধ করে দিয়ে প্রকল্পটি ব্যবহার করছি । (এটি ইচ্ছাকৃতভাবে পাইপ-ইনস্টলযোগ্য নয় কারণ আপনার স্থানীয় দোভাষী এবং এই জাতীয় বিল্ডের সাথে পরীক্ষা ব্যতীত সত্যই আপনি এটি ব্যবহার করা উচিত নয়))

import ctypes
import internals # https://github.com/abarnert/superhackyinternals/blob/master/internals.py

def print_function():
    print ("This cat was scared.")

def main():
    for c in print_function.__code__.co_consts:
        if isinstance(c, str):
            idx = c.find('cat')
            if idx != -1:
                # Too much to explain here; just guess and learn to
                # love the segfaults...
                p = internals.PyUnicodeObject.from_address(id(c))
                assert p.compact and p.ascii
                addr = id(c) + internals.PyUnicodeObject.utf8_length.offset
                buf = (ctypes.c_int8 * 3).from_address(addr + idx)
                buf[:3] = b'dog'

    print_function()

main()

আপনি যদি এই স্টাফ নিয়ে খেলতে চান intতবে কভারগুলির আওতায় সম্পূর্ণ সহজ str। এবং এটা অনুমান করার কি মান পরিবর্তন করে ভাঙ্গতে পারে অনেক সহজ 2করতে 1, ডান? আসলে, কল্পনা ভুলে যান, আসুন কেবল এটি করা ( superhackyinternalsআবার থেকে টাইপগুলি ব্যবহার করে ):

>>> n = 2
>>> pn = PyLongObject.from_address(id(n))
>>> pn.ob_digit[0]
2
>>> pn.ob_digit[0] = 1
>>> 2
1
>>> n * 3
3
>>> i = 10
>>> while i < 40:
...     i *= 2
...     print(i)
10
10
10

… ভান করুন যে কোড বাক্সে একটি অসীম দৈর্ঘ্যের স্ক্রোলবার রয়েছে।

আমি আইপিথনেও একই জিনিসটি চেষ্টা করেছি 2এবং প্রম্পটে প্রথমবার যখন মূল্যায়ন করার চেষ্টা করেছি তখন এটি একরকম নিরবচ্ছিন্ন অসীম লুপে চলে গেল। সম্ভবত এটি 2তার REPL লুপের কোনও কিছুর জন্য নম্বরটি ব্যবহার করছে , যখন স্টক ইন্টারপ্রেটার নেই?


11
@ cᴏʟᴅs কোড-মংটিটি যুক্তিযুক্ত যুক্তিযুক্ত পাইথন, যদিও আপনি সাধারণত অনেক ভাল কারণে (যেমন, কাস্টম অপ্টিমাইজারের মাধ্যমে বাইটকোড চালিয়ে যাচ্ছেন) কোড অবজেক্টগুলিকে স্পর্শ করতে চান। একটি অভ্যন্তরীণ স্টোরেজ অ্যাক্সেস করা PyUnicodeObject, অন্য দিকে, যে একটি পাইথন ইন্টারপ্রেটার এটা চালানো হবে অর্থে সম্ভবত সত্যিই শুধুমাত্র পাইথন আছে ...
abarnert

4
আপনার প্রথম কোড স্নিপেট উত্থাপন NameError: name 'arg' is not defined। আপনি বলতে চাইছেন: args = [arg.replace('cat', 'dog') if isinstance(arg, str) else arg for arg in args]? একটি তর্কসাপেক্ষে ভাল উপায় এই লিখতে হবে: args = [str(arg).replace('cat', 'dog') for arg in args]। আরেকটি, এমনকি খাটো, বিকল্প: args = map(lambda a: str(a).replace('cat', 'dog'), args)। এটির সাথে অতিরিক্ত উপকার রয়েছে যা argsঅলস (যা উপরের তালিকাটি বোঝার জেনারেটরের সাথে প্রতিস্থাপনের মাধ্যমেও সম্পন্ন করা যেতে পারে - *argsযে কোনও উপায়েই কাজ করে)।
কনস্টান্টিন

1
@ কেস হ্যাঁ, আইআইআরসি আমি কেবল PyUnicodeObjectস্ট্রাক্ট সংজ্ঞাটি ব্যবহার করছি , তবে উত্তরটির অনুলিপিটি করার পরে আমার মনে হবে যে কেবল পথ পাবে এবং আমি মনে করি রিডার এবং / অথবা উত্স মন্তব্যগুলি superhackyinternalsকীভাবে বাফার অ্যাক্সেস করবেন তা ব্যাখ্যা করার জন্য (কমপক্ষে পরের বার যত্ন নেওয়ার পরে আমাকে স্মরণ করিয়ে দেওয়ার জন্য যথেষ্ট পরিমাণে; নিশ্চিত নয় যে এটি অন্য কারও পক্ষে যথেষ্ট হবে কিনা ...), যা আমি এখানে toুকতে চাইনি। প্রাসঙ্গিক অংশটি হল কীভাবে লাইভ পাইথন অবজেক্ট থেকে এর PyObject *মাধ্যমে যেতে হয় ctypes। (এবং সম্ভবত পয়েন্টার পাটিগণিতের অনুকরণ, স্বয়ংক্রিয় char_pরূপান্তরগুলি এড়ানো ইত্যাদি)
অবার্নেট

1
@ jpmc26 আমি মনে করি না যে মডিউলগুলি আমদানি করার আগে আপনার এটি করা দরকার , যতক্ষণ আপনি এটি মুদ্রণের আগেই করেন। মডিউলগুলি প্রতিবার নাম অনুসন্ধান করবে, যদি না তারা স্পষ্টভাবে printকোনও নামের সাথে আবদ্ধ হয়। আপনিও কি NAME আবদ্ধ করতে পারেন printতাদের জন্য: import yourmodule; yourmodule.print = badprint
লিউজ

1
@ বার্নার্ট: আমি লক্ষ্য করেছি যে আপনি প্রায়শই এটি করার বিষয়ে সতর্ক করে দিয়েছেন (উদাঃ "আপনি কখনই আসলে এটি করতে চান না" , "মান পরিবর্তন করা কেন খারাপ ধারণা" ইত্যাদি)) এটি সম্ভবত পরিষ্কার নয় যে সম্ভবত কী ভুল হতে পারে (কটাক্ষ), আপনি কি এই বিষয়ে কিছুটা বর্ণনা করতে রাজি হবেন? এটি অন্ধভাবে চেষ্টা করার লোভিতদের জন্য এটি সম্ভবত সহায়তা করতে পারে।
l'L'l

37

বানর-প্যাচ print

printএটি একটি বিল্টিন ফাংশন তাই এটি মডিউলে (বা পাইথন 2 এ) printসংজ্ঞায়িত ফাংশনটি ব্যবহার করবে । সুতরাং আপনি যখনই কোনও বিল্টিন ফাংশনের আচরণটি পরিবর্তন করতে বা পরিবর্তন করতে চান আপনি কেবল সেই মডিউলটিতে নামটি পুনরায় অর্পণ করতে পারেন।builtins__builtin__

এই প্রক্রিয়া বলা হয় monkey-patching

# Store the real print function in another variable otherwise
# it will be inaccessible after being modified.
_print = print  

# Actual implementation of the new print
def custom_print(*args, **options):
    _print('custom print called')
    _print(*args, **options)

# Change the print function globally
import builtins
builtins.print = custom_print

এর পরে প্রতিটি কলই বাহ্যিক মডিউলে থাকলেও printতার মধ্য দিয়ে যাবে ।custom_printprint

তবে আপনি সত্যিই অতিরিক্ত পাঠ্য মুদ্রণ করতে চান না, আপনি মুদ্রিত পাঠ্যটি পরিবর্তন করতে চান। এটির দিকে যাওয়ার একটি উপায় হ'ল এটি মুদ্রিত স্ট্রিংয়ে প্রতিস্থাপন করা:

_print = print  

def custom_print(*args, **options):
    # Get the desired seperator or the default whitspace
    sep = options.pop('sep', ' ')
    # Create the final string
    printed_string = sep.join(args)
    # Modify the final string
    printed_string = printed_string.replace('cat', 'dog')
    # Call the default print function
    _print(printed_string, **options)

import builtins
builtins.print = custom_print

এবং সত্যই যদি আপনি চালান:

>>> def print_something():
...     print('This cat was scared.')
>>> print_something()
This dog was scared.

অথবা আপনি যদি এটি কোনও ফাইলে লিখেন:

test_file.py

def print_something():
    print('This cat was scared.')

print_something()

এবং এটি আমদানি করুন:

>>> import test_file
This dog was scared.
>>> test_file.print_something()
This dog was scared.

সুতরাং এটি সত্য হিসাবে কাজ করে।

তবে, আপনি যদি কেবলমাত্র অস্থায়ীভাবে বানর-প্যাচ মুদ্রণ করতে চান তবে আপনি এটি কোনও প্রসঙ্গে-পরিচালককে মোড়ানো করতে পারেন:

import builtins

class ChangePrint(object):
    def __init__(self):
        self.old_print = print

    def __enter__(self):
        def custom_print(*args, **options):
            # Get the desired seperator or the default whitspace
            sep = options.pop('sep', ' ')
            # Create the final string
            printed_string = sep.join(args)
            # Modify the final string
            printed_string = printed_string.replace('cat', 'dog')
            # Call the default print function
            self.old_print(printed_string, **options)

        builtins.print = custom_print

    def __exit__(self, *args, **kwargs):
        builtins.print = self.old_print

সুতরাং আপনি যখন চালনা করেন এটি কী প্রিন্ট করা হয় তা নির্ভর করে:

>>> with ChangePrint() as x:
...     test_file.print_something()
... 
This dog was scared.
>>> test_file.print_something()
This cat was scared.

সুতরাং আপনি কিভাবে printবানর-প্যাচিং দ্বারা "হ্যাক" করতে পারেন ।

এর পরিবর্তে লক্ষ্য পরিবর্তন করুন print

আপনি যদি স্বাক্ষরটি দেখেন তবে আপনি printএকটি fileযুক্তি লক্ষ্য করবেন যা sys.stdoutপূর্বনির্ধারিত। নোট যে এই একটি গতিশীল ডিফল্ট যুক্তি (এটা সত্যিই আপ দেখায় sys.stdoutআপনাকে কল প্রত্যেক সময় printএবং পাইথন স্বাভাবিক ডিফল্ট আর্গুমেন্ট মত নয়)। সুতরাং আপনি যদি পরিবর্তন করেন তবে sys.stdout printএটি পৃথক লক্ষ্যটিকে আরও বেশি সুবিধাজনকভাবে মুদ্রণ করবে যে পাইথন এছাড়াও একটি redirect_stdoutফাংশন সরবরাহ করে (পাইথন ৩.৪ থেকে, তবে পূর্ববর্তী পাইথন সংস্করণগুলির জন্য সমতুল্য ফাংশন তৈরি করা সহজ)।

খারাপ দিকটি এটি printমুদ্রণ না করে এমন বিবৃতিগুলির জন্য কাজ করবে না sys.stdoutএবং আপনার নিজের তৈরি stdoutসত্যিই সোজা নয়।

import io
import sys

class CustomStdout(object):
    def __init__(self, *args, **kwargs):
        self.current_stdout = sys.stdout

    def write(self, string):
        self.current_stdout.write(string.replace('cat', 'dog'))

তবে এটি কাজ করে:

>>> import contextlib
>>> with contextlib.redirect_stdout(CustomStdout()):
...     test_file.print_something()
... 
This dog was scared.
>>> test_file.print_something()
This cat was scared.

সারসংক্ষেপ

এর মধ্যে কয়েকটি পয়েন্ট ইতিমধ্যে @abarnet দ্বারা উল্লিখিত হয়েছে তবে আমি এই বিকল্পগুলি আরও বিশদে অনুসন্ধান করতে চেয়েছিলাম। বিশেষত কীভাবে এটি মডিউল জুড়ে ( কীভাবে builtins/ ব্যবহার করে __builtin__) পরিবর্তন করতে হয় এবং কীভাবে কীভাবে পরিবর্তনটিকে সাময়িকভাবে তৈরি করতে হয় (প্রসঙ্গের ব্যবস্থাপনার ব্যবহার করে)।


4
হ্যাঁ, এই প্রশ্নের সবচেয়ে কাছের জিনিসটি আসলে যে কারও কাছে করা উচিত তা হ'ল redirect_stdout, সুতরাং এর একটি স্পষ্ট উত্তর পাওয়া ভাল।
abarnert

6

কোনও printফাংশন থেকে সমস্ত আউটপুট ক্যাপচার এবং তারপরে প্রক্রিয়াজাতকরণের একটি সহজ উপায় হ'ল আউটপুট প্রবাহকে অন্য কোনও কিছুতে পরিবর্তন করা, যেমন একটি ফাইল।

আমি একটি PHPনামকরণের সম্মেলন ব্যবহার করব ( ob_start , ob_get_contents , ...)

from functools import partial
output_buffer = None
print_orig = print
def ob_start(fname="print.txt"):
    global print
    global output_buffer
    print = partial(print_orig, file=output_buffer)
    output_buffer = open(fname, 'w')
def ob_end():
    global output_buffer
    close(output_buffer)
    print = print_orig
def ob_get_contents(fname="print.txt"):
    return open(fname, 'r').read()

ব্যবহার:

print ("Hi John")
ob_start()
print ("Hi John")
ob_end()
print (ob_get_contents().replace("Hi", "Bye"))

মুদ্রণ করবে

হাই জন বাই বাই জন


5

এর ফ্রেম অন্তঃনির্ধারণের সাথে এটি একত্রিত করা যাক!

import sys

_print = print

def print(*args, **kw):
    frame = sys._getframe(1)
    _print(frame.f_code.co_name)
    _print(*args, **kw)

def greetly(name, greeting = "Hi")
    print(f"{greeting}, {name}!")

class Greeter:
    def __init__(self, greeting = "Hi"):
        self.greeting = greeting
    def greet(self, name):
        print(f"{self.greeting}, {name}!")

আপনি এই কৌশলটি কলিং ফাংশন বা পদ্ধতিটির সাথে প্রতিটি অভিবাদনকে উপস্থাপন করবেন। এটি লগিং বা ডিবাগিংয়ের জন্য খুব কার্যকর হতে পারে; বিশেষত এটি আপনাকে তৃতীয় পক্ষের কোডে "হাইজ্যাক" মুদ্রণ বিবৃতি দেয় lets

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.