আমি কীভাবে পাইথন অবজেক্টটি সঠিকভাবে পরিষ্কার করব?


461
class Package:
    def __init__(self):
        self.files = []

    # ...

    def __del__(self):
        for file in self.files:
            os.unlink(file)

__del__(self)উপরে একটি AttributeError ব্যতিক্রম ব্যর্থ হয়। আমি বুঝতে পারি পাইথন "গ্লোবাল ভেরিয়েবল" (এই প্রসঙ্গে সদস্য ডেটা?) এর অস্তিত্বের নিশ্চয়তা দেয় না__del__() । যদি এটি হয় এবং ব্যতিক্রমের কারণ এটি হয় তবে আমি কীভাবে নিশ্চিত করব যে অবজেক্টটি সঠিকভাবে ধ্বংসস্তূপ করবে?


3
আপনি যা লিঙ্ক করেছেন তা পড়া, গ্লোবাল ভেরিয়েবলগুলি চলে যাওয়া এখানে প্রয়োগ করার মতো মনে হয় না আপনি যখন প্রোগ্রামটি বেরিয়ে আসার সময় কথা বলছেন, সেই সময় আমি অনুমান করি যে আপনি যা সংযুক্ত করেছেন এটি অনুসারে এটি পসিবল হতে পারে যে ওএস মডিউলটি ইতিমধ্যে চলে গেছে। অন্যথায়, আমি মনে করি না এটি একটি __del __ () পদ্ধতিতে সদস্য ভেরিয়েবলের ক্ষেত্রে প্রযোজ্য।
কেভিন অ্যান্ডারসন

3
ব্যতিক্রমটি আমার প্রোগ্রামটি প্রস্থান হওয়ার অনেক আগে নিক্ষেপ করা হয়েছে। অ্যাট্রিবিউটআরর ব্যতিক্রমটি পাই পাইথন হ'ল এটি সেলফ.ফাইলেসকে প্যাকেজের বৈশিষ্ট্য হিসাবে স্বীকৃতি দেয় না। আমি এই ভুলটি পেয়ে যাচ্ছি, তবে যদি "গ্লোবালস" দ্বারা তারা ভেরিয়েবলগুলি গ্লোবাল পদ্ধতিতে বোঝায় না (তবে সম্ভবত শ্রেণিতে স্থানীয় হয়) তবে আমি জানি না কী কারণে এই ব্যতিক্রম ঘটে। গুগল ইঙ্গিত দেয় পাইথন __del __ (স্ব) বলার আগে সদস্য ডেটা পরিষ্কার করার অধিকার সংরক্ষণ করে।
উইলহেমটেল

1
পোস্ট করা কোডটি আমার পক্ষে (পাইথন 2.5 এর সাথে) কাজ করে বলে মনে হচ্ছে। আপনি কি ব্যর্থ হচ্ছে এমন আসল কোডটি পোস্ট করতে পারেন - বা একটি সরলীকৃত (আরও ভাল সংস্করণ যা এখনও ত্রুটির কারণ হয়?
সিলভারফিশ

@ উইলহেলমটেল আপনি কি আরও দৃ concrete় উদাহরণ দিতে পারেন? আমার সমস্ত পরীক্ষায়, ডেল ডেস্ট্রাক্টর পুরোপুরি কাজ করে।
অজানা

7
যদি কেউ জানতে চান: এই নিবন্ধটি কেন __del__প্রতিরূপ হিসাবে ব্যবহার করা উচিত নয় তা বিশদ করে __init__। (অর্থাৎ এটি __init__কোনও নির্মাণকারীর অর্থে কোনও "ডেস্ট্রাক্টর" নয়
ফ্র্যাঙ্কলিন

উত্তর:


619

আমি যে withসংস্থানগুলি পরিষ্কার করতে হবে তা পরিচালনার জন্য পাইথনের বক্তব্যটি ব্যবহার করার পরামর্শ দেব । সুস্পষ্ট close()বিবৃতি ব্যবহারের ক্ষেত্রে সমস্যাটি হ'ল লোকেরা যখন একেবারেই কল করতে ভুলে যায় বা finallyকোনও ব্যতিক্রম ঘটে তখন রিসোর্স ফাঁস রোধ করার জন্য এটি একটি ব্লকের মধ্যে রাখতে ভুলে যাওয়া সম্পর্কে আপনাকে চিন্তিত হতে হবে ।

withবিবৃতিটি ব্যবহার করতে , নিম্নলিখিত পদ্ধতিগুলি সহ একটি শ্রেণী তৈরি করুন:

  def __enter__(self)
  def __exit__(self, exc_type, exc_value, traceback)

উপরে আপনার উদাহরণে, আপনি ব্যবহার করতে চাই

class Package:
    def __init__(self):
        self.files = []

    def __enter__(self):
        return self

    # ...

    def __exit__(self, exc_type, exc_value, traceback):
        for file in self.files:
            os.unlink(file)

তারপরে, যখন কেউ আপনার ক্লাসটি ব্যবহার করতে চেয়েছিল, তারা নিম্নলিখিতগুলি করত:

with Package() as package_obj:
    # use package_obj

ভেরিয়েবল প্যাকেজ_বজ প্যাকেজ টাইপের একটি উদাহরণ হবে (এটি __enter__পদ্ধতি দ্বারা প্রত্যাবর্তিত মান )। এর __exit__পদ্ধতিটি স্বয়ংক্রিয়ভাবে ডাকা হবে, কোনও ব্যতিক্রম ঘটে কিনা তা নির্বিশেষে।

এমনকি আপনি এই পদ্ধতির আরও একধাপ এগিয়ে নিতে পারেন। উপরের উদাহরণে, কেউ کلاসটি ব্যবহার না করেই প্যাকেজটির নির্মাণকারী ব্যবহার করে ইনস্ট্যান্ট করতে পারে with। আপনি তা চান না। আপনি প্যাকেজ রিসোর্স ক্লাস তৈরি করে এটি ঠিক করতে পারেন যা পদ্ধতি __enter__এবং __exit__পদ্ধতিগুলি নির্ধারণ করে । তারপরে, প্যাকেজ ক্লাসটি __enter__পদ্ধতির অভ্যন্তরে কঠোরভাবে সংজ্ঞায়িত করা হবে এবং ফিরে আসবে। এইভাবে, কলার কোনও withবিবৃতি ব্যবহার না করে প্যাকেজ ক্লাসটি ইনস্ট্যান্ট করতে পারে না :

class PackageResource:
    def __enter__(self):
        class Package:
            ...
        self.package_obj = Package()
        return self.package_obj

    def __exit__(self, exc_type, exc_value, traceback):
        self.package_obj.cleanup()

আপনি এটি নিম্নলিখিত হিসাবে ব্যবহার করবেন:

with PackageResource() as package_obj:
    # use package_obj

35
প্রযুক্তিগতভাবে বলতে গেলে, কেউ প্যাকেজ রিসোর্স () __ লিখুন __ () স্পষ্টভাবে কল করতে পারে এবং এভাবে এমন একটি প্যাকেজ তৈরি করতে পারে যা কখনই চূড়ান্ত হয় না ... তবে তাদের অবশ্যই কোডটি ভাঙার চেষ্টা করতে হবে। সম্ভবত উদ্বিগ্ন হওয়ার মতো কিছু নয়।
ডেভিড জেড

3
যাইহোক, আপনি যদি পাইথন 2.5 ব্যবহার করেন, আপনার স্টেটমেন্ট সহ স্টেটমেন্টটি ব্যবহার করতে সক্ষম হতে ভবিষ্যতের আমদানি থেকে করতে হবে।
ক্লিন্ট মিলার

2
আমি একটি নিবন্ধ পেয়েছি যা __del __ () এটি করে এমনভাবে কাজ করে এবং একটি প্রসঙ্গ পরিচালকের সমাধান ব্যবহার করে বিশ্বাসযোগ্যতা প্রদান করতে সহায়তা করে: andy-pearce.com/blog/posts/2013/Apr/python-destructor-drawbacks
একনোমেগা

2
আপনি যদি প্যারামিটারগুলি পাস করতে চান তবে কীভাবে সুন্দর এবং ক্লিন কনস্ট্রাক্ট ব্যবহার করবেন? আমি করতে চাইwith Resource(param1, param2) as r: # ...
snooze92

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

48

মানক উপায়টি হ'ল atexit.register:

# package.py
import atexit
import os

class Package:
    def __init__(self):
        self.files = []
        atexit.register(self.cleanup)

    def cleanup(self):
        print("Running cleanup...")
        for file in self.files:
            print("Unlinking file: {}".format(file))
            # os.unlink(file)

তবে আপনার মনে রাখা উচিত যে Packageপাইথন সমাপ্ত না হওয়া পর্যন্ত এটি তৈরি হওয়া সমস্ত দৃষ্টান্ত বহাল রাখবে ।

উপরের কোডটি ডেমো ব্যবহার করে প্যাকেজ.পি হিসাবে সংরক্ষণ করা হয়েছে :

$ python
>>> from package import *
>>> p = Package()
>>> q = Package()
>>> q.files = ['a', 'b', 'c']
>>> quit()
Running cleanup...
Unlinking file: a
Unlinking file: b
Unlinking file: c
Running cleanup...

2
Axxit.register পদ্ধতির সুন্দর জিনিসটি কি আপনাকে ক্লাসের ব্যবহারকারী কী ব্যবহার করবেন তা নিয়ে চিন্তা করতে হবে না (তারা কি ব্যবহার করেছিল with? তারা কি স্পষ্টভাবে কল করেছিল __enter__?) অজগলের আগে যদি আপনার পরিষ্কার-পরিচ্ছন্নতার প্রয়োজন হয় তবে ডাউনসাইড অবশ্যই হয় প্রস্থান, এটি কাজ করবে না। আমার ক্ষেত্রে, যখন বিষয়টি সুযোগের বাইরে চলে যায় বা পাইথনটি বের না হওয়া অবধি এটি হয় তবে তা আমি বিবেচনা করি না। :)
hlongmore

আমি প্রবেশ এবং প্রস্থান এবং যুক্ত করতে atexit.register(self.__exit__)পারি?
ম্যারাডিও

@myradio আমি কিভাবে দেখছি না যে এটি কার্যকর হবে? আপনি কি সমস্ত ক্লিনআপ লজিক ভিতরে রাখতে পারবেন না __exit__, এবং একটি প্রসঙ্গ পরিচালক ব্যবহার করতে পারবেন? এছাড়াও, __exit__অতিরিক্ত যুক্তি (যেমন __exit__(self, type, value, traceback)) নেয় , যাতে আপনার পক্ষে এটির প্রয়োজন হয়। যেভাবেই হোক, মনে হচ্ছে আপনার কোনও পৃথক প্রশ্ন এসওতে পোস্ট করা উচিত, কারণ আপনার ব্যবহারের ক্ষেত্রে এটি অস্বাভাবিক বলে মনে হচ্ছে?
ostrokach

33

ক্লিন্টের উত্তরের পরিশিষ্ট হিসাবে , আপনি এগুলি PackageResourceব্যবহার করে সহজ করতে পারবেন contextlib.contextmanager:

@contextlib.contextmanager
def packageResource():
    class Package:
        ...
    package = Package()
    yield package
    package.cleanup()

বিকল্পভাবে, যদিও পাইথোনিক হিসাবে সম্ভবত না, আপনি ওভাররাইড করতে পারেন Package.__new__:

class Package(object):
    def __new__(cls, *args, **kwargs):
        @contextlib.contextmanager
        def packageResource():
            # adapt arguments if superclass takes some!
            package = super(Package, cls).__new__(cls)
            package.__init__(*args, **kwargs)
            yield package
            package.cleanup()

    def __init__(self, *args, **kwargs):
        ...

এবং সহজভাবে ব্যবহার with Package(...) as package

জিনিসগুলি ছোট করার জন্য, আপনার ক্লিনআপ ফাংশনটির নাম দিন closeএবং ব্যবহার করুন contextlib.closing, এক্ষেত্রে আপনি হয় অশোধিত Packageক্লাসটি মাধ্যমে ব্যবহার করতে পারেন with contextlib.closing(Package(...))বা এর সহজটিকে ওভাররাইড __new__করতে পারেন

class Package(object):
    def __new__(cls, *args, **kwargs):
        package = super(Package, cls).__new__(cls)
        package.__init__(*args, **kwargs)
        return contextlib.closing(package)

এবং এই নির্মাতা উত্তরাধিকার সূত্রে প্রাপ্ত, সুতরাং আপনি কেবল উত্তরাধিকারী হিসাবে নিতে পারেন, যেমন

class SubPackage(Package):
    def close(self):
        pass

1
এটা সত্যিই দারুন. আমি বিশেষ করে শেষ উদাহরণটি পছন্দ করি। এটি দুর্ভাগ্যজনক যে আমরা Package.__new__()তবে পদ্ধতির চার-লাইনের বয়লারপ্লেট এড়াতে পারি না । অথবা আমরা পারে। আমরা সম্ভবত আমাদের জন্য সেই বয়লারপ্লেটকে জেনারাইজ করে কোনও শ্রেণীর সাজসজ্জার বা মেটাক্লাসের সংজ্ঞা দিতে পারি। পাইথোনিক চিন্তার জন্য খাদ্য।
সিসিল কারি

@ সিসিলক্রোরি ধন্যবাদ, এবং ভাল পয়েন্ট। উত্তরাধিকার সূত্রে প্রাপ্ত Packageকোনও শ্রেণীরও এটি করা উচিত (যদিও আমি এখনও এটি পরীক্ষা করি নি), সুতরাং কোনও মেটাক্লাসের প্রয়োজন হবে না। আমি করেছি অতীতে metaclasses ব্যবহার করতে কিছু প্রশংসনীয় জানতে আগ্রহী উপায়ে ... পাওয়া
Tobias Kienzler

@ সিসিলক্রোরি প্রকৃতপক্ষে, নির্মাণকারীর উত্তরাধিকার সূত্রে প্রাপ্ত, সুতরাং আপনি পরিবর্তে Package(বা আরও ভাল নামে একটি শ্রেণি Closing) আপনার শ্রেণি পিতা বা মাতা হিসাবে ব্যবহার করতে পারেন object। তবে আমাকে জিজ্ঞাসা করবেন না কীভাবে একাধিক উত্তরাধিকার এতে
গন্ডগোল করে

17

আমি মনে করি না যে উদাহরণস্বরূপ সদস্যদের __del__ডাকা হওয়ার আগেই অপসারণ করা সম্ভব । আমার অনুমানটি হ'ল আপনার নির্দিষ্ট অ্যাট্রিবিউটআরারের কারণটি অন্য কোথাও রয়েছে (সম্ভবত আপনি ভুল করে সেলফ.ফাইলে অন্য কোথাও অপসারণ করেছেন)।

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


5
সঙ্গে অবজেক্টস __del__আবর্জনা সংগ্রহ করা যায় যদি সঙ্গে অন্যান্য বস্তু থেকে তাদের রেফারেন্স গণনা __del__শূন্য এবং তারা পৌঁছানো যাবে না। এর অর্থ যদি আপনার সাথে অবজেক্টগুলির মধ্যে একটি রেফারেন্স চক্র থাকে তবে __del__সেগুলির কোনওটিই সংগ্রহ করা হবে না। অন্য যে কোনও মামলা প্রত্যাশা অনুযায়ী সমাধান করা উচিত।
কলিন

"পাইথন ৩.৪ দিয়ে শুরু করা, __ডেল __ () পদ্ধতিগুলি রেফারেন্স চক্রটিকে আবর্জনা সংগ্রহ করা থেকে আর আটকাবে না, এবং মডিউল গ্লোবালগুলি দোভাষী বন্ধের সময় আর কারও কাছেই বাধ্য করা হবে না So সুতরাং সিপিথনের কোনও সমস্যা ছাড়াই এই কোডটি কাজ করা উচিত" " - docs.python.org/3.6/library/…
টমাসজ গেন্ডার

14

1
এটি আজ ব্যবহার করুন এবং এটি নির্বিঘ্নে কাজ করে, অন্যান্য সমাধানের চেয়ে ভাল। আমার মাল্টিপ্রসেসিং-ভিত্তিক যোগাযোগকারী শ্রেণী রয়েছে যা একটি সিরিয়াল বন্দর খোলে এবং তারপরে stop()পোর্টগুলি এবং join()প্রক্রিয়াগুলি বন্ধ করার জন্য আমার একটি পদ্ধতি রয়েছে । যাইহোক, প্রোগ্রামগুলি অপ্রত্যাশিতভাবে প্রস্থান করা হলে তাকে stop()ডাকা হয় না - আমি এটি একটি ফাইনালাইজার দিয়ে সমাধান করেছি। তবে যাই হোক না কেন আমি _finalizer.detach()স্টপ পদ্ধতিতে ফোন করি যাতে এটি দু'বার কল করা না যায় (ম্যানুয়ালি এবং পরে আবার ফাইনালাইজারের মাধ্যমে)।
বোজন পি।

3
আইএমও, এটি সত্যিই সেরা উত্তর। এটি আবর্জনা সংগ্রহের সময় পরিষ্কারের সম্ভাবনাটি প্রস্থান করার সময় পরিষ্কার করার সম্ভাবনার সাথে একত্রিত করে। সতর্কবাণীটি হল যে অজগর ২.7 এর দুর্বলতা নেই f
hlongmore

12

আমি মনে করি সমস্যাটি __init__যদি দেখানো থেকে বেশি কোড থাকে তবে হতে পারে ?

__del____init__সঠিকভাবে কার্যকর করা হয়নি বা ব্যতিক্রম ছুঁড়ে ফেলা না হলেও তাকে ডাকা হবে ।

সূত্র


2
খুব সম্ভবত লাগছে। ব্যবহার __del__করার সময় এই সমস্যাটি এড়ানোর সর্বোত্তম উপায় হ'ল ক্লাস-স্তরে সমস্ত সদস্যকে স্পষ্টভাবে ঘোষণা করা, এটি __init__ব্যর্থ হওয়া সত্ত্বেও তারা সর্বদা উপস্থিত রয়েছে তা নিশ্চিত করে । প্রদত্ত উদাহরণে, files = ()কাজ করবে, যদিও বেশিরভাগই আপনি কেবল নিয়োগ করেন None; উভয় ক্ষেত্রেই, আপনাকে এখনও আসল মানটি নির্ধারণ করতে হবে __init__
সেরেন ল্যাভবার্গ 8:46

11

এখানে একটি সর্বনিম্ন কার্যকারী কঙ্কাল রয়েছে:

class SkeletonFixture:

    def __init__(self):
        pass

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        pass

    def method(self):
        pass


with SkeletonFixture() as fixture:
    fixture.method()

গুরুত্বপূর্ণ: স্ব স্ব ফিরে


আপনি যদি আমার মতো হন এবং return selfঅংশটি ( ক্লিন্ট মিলারের সঠিক উত্তরের ) উপেক্ষা করে থাকেন তবে আপনি এই আজেবাজে তাকাবেন:

Traceback (most recent call last):
  File "tests/simplestpossible.py", line 17, in <module>                                                                                                                                                          
    fixture.method()                                                                                                                                                                                              
AttributeError: 'NoneType' object has no attribute 'method'

আশা করি এটি পরবর্তী ব্যক্তিকে সহায়তা করবে।


8

কেবলমাত্র চেষ্টা করে / বাদ দিয়ে বিবৃতি দিয়ে আপনার ডেস্ট্রাক্টরকে মুড়ে রাখুন এবং যদি আপনার গ্লোবালগুলি ইতিমধ্যে নিষ্পত্তি করা হয় তবে এটি ব্যতিক্রম হবে না।

সম্পাদন করা

এটা চেষ্টা কর:

from weakref import proxy

class MyList(list): pass

class Package:
    def __init__(self):
        self.__del__.im_func.files = MyList([1,2,3,4])
        self.files = proxy(self.__del__.im_func.files)

    def __del__(self):
        print self.__del__.im_func.files

এটি ডেল ফাংশনে ফাইল তালিকার স্টাফ করবে যা কল করার সময় উপস্থিত থাকার গ্যারান্টিযুক্ত। দুর্বলতম প্রক্সিটি পাইথনকে বা নিজের থেকে কোনওভাবে সেলফ.ফাইলেস ভেরিয়েবল মুছে ফেলা থেকে বিরত রাখা (যদি এটি মুছে ফেলা হয় তবে এটি মূল ফাইল তালিকাকে প্রভাবিত করবে না)। যদি ভেরিয়েবলের আরও রেফারেন্স থাকা সত্ত্বেও এটি মুছে ফেলা হয় না তবে আপনি প্রক্সি এনক্যাপসুলেশনটি সরাতে পারেন।


2
সমস্যাটি হ'ল যদি সদস্যের ডেটা চলে যায় তবে আমার জন্য খুব দেরী। আমার সেই ডেটা দরকার উপরের আমার কোডটি দেখুন: কোন ফাইলগুলি অপসারণ করতে হবে তা জানতে আমার ফাইলের নাম প্রয়োজন। যদিও আমি নিজের কোডটি সরলীকরণ করেছি, আমার নিজের আরও পরিষ্কার করার দরকার আছে এমন আরও অন্যান্য ডেটা রয়েছে (অর্থাত্ দোভাষী কীভাবে পরিষ্কার করতে পারবেন না)।
উইলহেমটেল

4

দেখে মনে হচ্ছে এটি করার মতো মূর্খ উপায়টি কোনও close()পদ্ধতি সরবরাহ করা (বা অনুরূপ), এবং এটিকে স্পষ্টভাবে কল করুন।


20
এটি আমি আগে ব্যবহার করেছি, তবে আমি এটির সাথে অন্যান্য সমস্যার মধ্যে পড়েছিলাম। অন্যান্য গ্রন্থাগারগুলির দ্বারা পুরো জায়গা জুড়ে ব্যতিক্রম ব্যতীত, আমার ত্রুটির ক্ষেত্রে জঞ্জাল পরিষ্কার করতে পাইথনের সহায়তা প্রয়োজন। বিশেষত, আমার জন্য ডেস্ট্রাক্টরকে কল করার জন্য পাইথন দরকার, কারণ অন্যথায় কোডটি দ্রুত ব্যবস্থাপনযোগ্য না হয়ে যায় এবং আমি অবশ্যই একটি প্রস্থান পয়েন্টটি ভুলে যাব যেখানে ক্লোজ () এ কল করা উচিত call
wilhelmtell
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.