পাইথনে অপরিবর্তনীয় বস্তুটি কীভাবে তৈরি করা যায়?


179

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

class Immutable(tuple):

    def __new__(cls, a, b):
        return tuple.__new__(cls, (a, b))

    @property
    def a(self):
        return self[0]

    @property
    def b(self):
        return self[1]

    def __str__(self):
        return "<Immutable {0}, {1}>".format(self.a, self.b)

    def __setattr__(self, *ignored):
        raise NotImplementedError

    def __delattr__(self, *ignored):
        raise NotImplementedError

কিন্তু তারপর আপনার অ্যাক্সেস রয়েছে aএবং bএর মাধ্যমে ভেরিয়েবল self[0]এবং self[1]যা বিরক্তিকর।

খাঁটি পাইথনে এটি কি সম্ভব? যদি তা না হয় তবে আমি কীভাবে এটি সি এক্সটেনশন দিয়ে করব?

(যে উত্তরগুলি কেবল পাইথন 3 এ কাজ করে সেগুলি গ্রহণযোগ্য)।

হালনাগাদ:

সুতরাং subclassing tuple পথ বিশুদ্ধ পাইথন, যা ভাল দ্বারা তথ্য অ্যাক্সেস অতিরিক্ত সম্ভাবনা ছাড়া কাজ করে এটা করতে হয় [0], [1]ইত্যাদি সুতরাং, এই প্রশ্ন যে সব হারিয়েছে হাওটুর এটি "সঠিকভাবে" সি, না হয় সম্পূর্ণ করার জন্য যা আমি সন্দেহ করি যে কোনও সরল geititemবা setattributeইত্যাদি বাস্তবায়ন না করে এটি বেশ সহজ হবে তবে আমি নিজে এটি না করে আমি তার জন্য অনুদানের প্রস্তাব দিই, কারণ আমি অলস। :)


2
আপনার কোড .aএবং এর মাধ্যমে বৈশিষ্ট্যগুলিতে অ্যাক্সেসের সুবিধা দেয় না .b? বৈশিষ্ট্যগুলি সমস্ত কিছুর জন্য বিদ্যমান বলে মনে হয়।
সোভেন মারনাচ

1
@ স্পেন মারনাচ: হ্যাঁ, তবে [0] এবং [1] এখনও কাজ করছে এবং তারা কেন করবে? আমি তাদের চাই না। :) গুণাবলী সহ একটি অপরিবর্তনীয় বস্তুর ধারণা বাজে? :-)
লেনার্ট রেজেব্রো

2
কেবলমাত্র অন্য একটি নোট: NotImplementedকেবল সমৃদ্ধ তুলনাগুলির জন্য একটি ফিরতি মান হিসাবে বোঝানো হয়েছে। এর জন্য কোনও রিটার্ন মান __setatt__()হ'ল বরং অর্থহীন, যেহেতু আপনি সাধারণত এটি মোটেও দেখতে পাবেন না। কোডটি immutable.x = 42নীরবে কিছুই করবে না। TypeErrorপরিবর্তে আপনার উত্থাপন করা উচিত ।
সোভেন মারনাচ

1
@ স্পেন মারনাচ: ঠিক আছে, আমি অবাক হয়েছিলাম, কারণ আমি ভেবেছিলাম আপনি এই পরিস্থিতিতে নোটইম্প্লেমেন্টযুক্ত বাড়াতে পারবেন, তবে এটি একটি অদ্ভুত ত্রুটি দেয়। সুতরাং আমি এটি পরিবর্তে ফিরে এসেছি, এবং এটি কাজ করে বলে মনে হয়েছিল। আপনি এটি ব্যবহার করতে দেখে একবার টাইপররারের স্পষ্ট ধারণা তৈরি হয়েছিল।
লেনার্ট রেজেব্রো

1
@Lennart: আপনি বাড়াতে পারে NotImplementedError, কিন্তু TypeErrorকি একটি tuple উত্থাপন যদি আপনি এটি পরিবর্তন করতে চেষ্টা করুন।
সোভেন মারনাচ

উত্তর:


115

তবুও আরেকটি সমাধান আমি কেবল ভেবেছিলাম: আপনার আসল কোডের মতো আচরণ করার সহজ উপায়

Immutable = collections.namedtuple("Immutable", ["a", "b"])

এটা তোলে সমস্যা হল বৈশিষ্ট্যাবলী মাধ্যমে অ্যাক্সেস করা যেতে পারে সমাধান নেই [0]ইত্যাদি, কিন্তু অন্তত এটা যথেষ্ট খাটো এবং সঙ্গে সামঞ্জস্যপূর্ণ হচ্ছে অতিরিক্ত সুবিধা প্রদান করে pickleএবং copy

namedtupleআমি এই উত্তরে যা বর্ণনা করেছি তার অনুরূপ একটি প্রকার তৈরি করে , অর্থাত্‍ উত্স থেকে tupleএবং ব্যবহার __slots__। এটি পাইথন ২.6 বা তারও উপরে পাওয়া যায়।


7
হস্ত-লিখিত অ্যানালগের তুলনায় এই বৈকল্পিকের সুবিধা (পাইথন 2.5 তেও ( কোডের verboseপ্যারামিটার ব্যবহার করে namedtupleসহজেই উত্পন্ন করা যায়)) এর একক ইন্টারফেস / বাস্তবায়ন namedtupleহ'ল কয়েক দশকের তুলনায় এতটা স্বল্প লিখিত ইন্টারফেস / বাস্তবায়ন কি প্রায় একই জিনিস।
jfs

2
ঠিক আছে, আপনি "সেরা উত্তর" পেয়েছেন, কারণ এটি করার সবচেয়ে সহজ উপায়। সংক্ষিপ্ত Cython বাস্তবায়ন দেওয়ার জন্য অনুগ্রহ অর্জন করে সেবাস্তিয়ান। চিয়ার্স!
লেনার্ট রেজেব্রো

1
অপরিবর্তনীয় বস্তুর আর একটি বৈশিষ্ট্য হ'ল আপনি যখন এগুলি কোনও ফাংশনের মাধ্যমে প্যারামিটার হিসাবে পাস করেন, সেগুলি অন্য রেফারেন্সের পরিবর্তে মান দ্বারা অনুলিপি করা হয়। হায় namedtupleযখন ফাংশন মাধ্যমে গৃহীত মান অনুলিপি করা কইলাম?
hlin117

4
@ hlin117: প্রতিটি প্যারামিটারটি পাইথনের কোনও বস্তুর রেফারেন্স হিসাবে পাস করা হয় তা নির্বিশেষে বা অপরিবর্তনীয় তা নির্বিশেষে। অপরিবর্তনীয় বস্তুর জন্য, অনুলিপি করা বিশেষত অর্থহীন হবে - যেহেতু আপনি যেভাবেই বস্তুটি পরিবর্তন করতে পারবেন না, আপনি ঠিক পাশাপাশি মূল বস্তুর একটি রেফারেন্সও দিতে পারেন।
সোভেন মারনাচ

আপনি কি ক্লাসের অভ্যন্তরীণভাবে নেমটুপলটি অবজেক্টটি বাহ্যিকভাবে ইনস্ট্যান্ট করার পরিবর্তে ব্যবহার করতে পারবেন? আমি অজগর থেকে খুব নতুন কিন্তু আপনার অন্য উত্তরের সুবিধাটি হ'ল আমি ক্লাসটি বিশদটি গোপন করতে পারি এবং optionচ্ছিক পরামিতিগুলির মতো জিনিসগুলির ক্ষমতাও রাখতে পারি। আমি যদি কেবলমাত্র এই উত্তরটি লক্ষ্য করি তবে মনে হয় আমার ক্লাস ইনস্ট্যানিয়েট নামের টিপলস ব্যবহার করে এমন সমস্ত কিছু আমার থাকা দরকার। উভয় উত্তরের জন্য আপনাকে ধন্যবাদ।
আসফ

78

এটি করার সবচেয়ে সহজ উপায়টি হ'ল __slots__:

class A(object):
    __slots__ = []

উদাহরণগুলি Aএখন অপরিবর্তনীয়, যেহেতু আপনি সেগুলিতে কোনও বৈশিষ্ট্য সেট করতে পারবেন না।

আপনি যদি ক্লাসের উদাহরণগুলিতে ডেটা ধারণ করতে চান তবে আপনি এগুলি থেকে প্রাপ্তগুলির সাথে একত্রিত করতে পারেন tuple:

from operator import itemgetter
class Point(tuple):
    __slots__ = []
    def __new__(cls, x, y):
        return tuple.__new__(cls, (x, y))
    x = property(itemgetter(0))
    y = property(itemgetter(1))

p = Point(2, 3)
p.x
# 2
p.y
# 3

সম্পাদনা করুন : আপনি যদি কোনও সূচকে বাদ দিতে চান তবে আপনি ওভাররাইড করতে পারেন __getitem__():

class Point(tuple):
    __slots__ = []
    def __new__(cls, x, y):
        return tuple.__new__(cls, (x, y))
    @property
    def x(self):
        return tuple.__getitem__(self, 0)
    @property
    def y(self):
        return tuple.__getitem__(self, 1)
    def __getitem__(self, item):
        raise TypeError

মনে রাখবেন যে আপনি operator.itemgetterএই ক্ষেত্রে বৈশিষ্ট্যগুলির জন্য ব্যবহার করতে পারবেন না , কারণ এটি এর Point.__getitem__()পরিবর্তে নির্ভর করে tuple.__getitem__()। ফুয়ারথেরমোর এটি ব্যবহারকে আটকাবে না tuple.__getitem__(p, 0), তবে কীভাবে এটি একটি সমস্যা তৈরি করতে হবে তা আমি খুব কমই ভাবতে পারি।

আমি মনে করি না যে অপরিবর্তনীয় বস্তু তৈরির "সঠিক" উপায়টি একটি সি এক্সটেনশান লিখছে। পাইথন সাধারণত লাইব্রেরি প্রয়োগকারী এবং গ্রন্থাগার ব্যবহারকারীদের উপর প্রাপ্তবয়স্কদের সম্মতি দেয় এবং তাদের ইন্টারফেসটি সত্যিকার অর্থে প্রয়োগ করার পরিবর্তে ডকুমেন্টেশনে ইন্টারফেসটি স্পষ্টভাবে বলা উচিত। এ কারণেই আমি কোনও সমস্যা __setattr__()ডেকে object.__setattr__()একটি ওভাররাইডকে আটকানোর সম্ভাবনা বিবেচনা করি না । যদি কেউ এটি করে তবে এটি তার নিজের ঝুঁকিতে রয়েছে।


1
এর চেয়ে tupleএখানে কী ব্যবহার করা ভাল হবে না ? (কেবল স্পষ্ট করে)__slots__ = ()__slots__ = []
ব্যবহারকারীর 25253

1
@ সুখবীর: আমি মনে করি এটি মোটেই গুরুত্বপূর্ণ নয়। আপনি কেন একটি টিপল পছন্দ করবেন?
সোভেন মারনাচ

1
@ সোভেন: আমি সম্মত হলাম এটির কোনও ব্যাপার হবে না (গতির অংশ ব্যতীত, যা আমরা উপেক্ষা করতে পারি), তবে আমি এটি এইভাবে ভেবেছিলাম: __slots__ঠিক কি পরিবর্তন হবে না? এর উদ্দেশ্যটি হ'ল একবারের জন্য কোন বৈশিষ্ট্যগুলি সেট করা যায় তা চিহ্নিত করা। তাহলে কি এ জাতীয় ক্ষেত্রে tupleখুব প্রাকৃতিক পছন্দ বলে মনে হচ্ছে না ?
user225312

5
তবে খালি দিয়ে __slots__আমি কোনও বৈশিষ্ট্য নির্ধারণ করতে পারি না । এবং যদি আমার কাছে থাকে __slots__ = ('a', 'b')তবে ক এবং বি বৈশিষ্ট্যগুলি এখনও পরিবর্তনযোগ্য।
লেনার্ট রেজেব্রো

তবে আপনার সমাধান ওভাররাইডের চেয়ে ভাল __setattr__তাই এটি আমার থেকে উন্নতি। +1 :)
লেনার্ট রেজেব্রো

50

..এটি সিতে "সঠিকভাবে" করুন

আপনি পাইথনের জন্য একটি এক্সটেনশন প্রকার তৈরি করতে সাইথন ব্যবহার করতে পারেন :

cdef class Immutable:
    cdef readonly object a, b
    cdef object __weakref__ # enable weak referencing support

    def __init__(self, a, b):
        self.a, self.b = a, b

এটি পাইথন 2.x এবং 3 উভয়ই কাজ করে।

টেস্ট

# compile on-the-fly
import pyximport; pyximport.install() # $ pip install cython
from immutable import Immutable

o = Immutable(1, 2)
assert o.a == 1, str(o.a)
assert o.b == 2

try: o.a = 3
except AttributeError:
    pass
else:
    assert 0, 'attribute must be readonly'

try: o[1]
except TypeError:
    pass
else:
    assert 0, 'indexing must not be supported'

try: o.c = 1
except AttributeError:
    pass
else:
    assert 0, 'no new attributes are allowed'

o = Immutable('a', [])
assert o.a == 'a'
assert o.b == []

o.b.append(3) # attribute may contain mutable object
assert o.b == [3]

try: o.c
except AttributeError:
    pass
else:
    assert 0, 'no c attribute'

o = Immutable(b=3,a=1)
assert o.a == 1 and o.b == 3

try: del o.b
except AttributeError:
    pass
else:
    assert 0, "can't delete attribute"

d = dict(b=3, a=1)
o = Immutable(**d)
assert o.a == d['a'] and o.b == d['b']

o = Immutable(1,b=3)
assert o.a == 1 and o.b == 3

try: object.__setattr__(o, 'a', 1)
except AttributeError:
    pass
else:
    assert 0, 'attributes are readonly'

try: object.__setattr__(o, 'c', 1)
except AttributeError:
    pass
else:
    assert 0, 'no new attributes'

try: Immutable(1,c=3)
except TypeError:
    pass
else:
    assert 0, 'accept only a,b keywords'

for kwd in [dict(a=1), dict(b=2)]:
    try: Immutable(**kwd)
    except TypeError:
        pass
    else:
        assert 0, 'Immutable requires exactly 2 arguments'

আপনি যদি সূচী সমর্থন সমর্থন না করেন তবে @ স্যভেন মার্নাচেরcollections.namedtuple পরামর্শ দেওয়া পছন্দনীয় :

Immutable = collections.namedtuple("Immutable", "a b")

@ লেনার্ট: namedtuple(বা আরও স্পষ্টভাবে ফাংশন দ্বারা প্রত্যাবর্তিত প্রকারের namedtuple()) উদাহরণগুলি অপরিবর্তনীয়। স্পষ্টভাবে.
সোভেন মারনাচ

@ লেনার্ট রেজেব্রো: namedtupleসমস্ত পরীক্ষা পাস করে (ইনডেক্সিং সমর্থন ব্যতীত)। আমি কী প্রয়োজন মিস করলাম?
jfs

হ্যাঁ, আপনি ঠিক বলেছেন, আমি একটি নেমটুপল টাইপ তৈরি করেছি, এটি ইনস্ট্যান্ট করেছিলাম এবং তারপরে উদাহরণটির পরিবর্তে টাইপটি পরীক্ষা করেছি। হেহ। :-)
লেনার্ট রেজেব্রো 21

আমি জিজ্ঞাসা করতে পারি কেন এখানে কেন একজনকে দুর্বল রেফারেন্সিংয়ের প্রয়োজন হবে?
ম্যাকসিনিক্স

1
@ এমসি সাইনেক্স: অন্যথায়, দুর্বলদের সংগ্রহগুলিতে অবজেক্টগুলি ব্যবহার করা যাবে না। পাইথনে ঠিক কী __weakref__?
jfs

40

আর একটি ধারণা হ'ল সম্পূর্ণরূপে কনস্ট্রাক্টরটিকে অস্বীকার করা __setattr__এবং ব্যবহার object.__setattr__করা:

class Point(object):
    def __init__(self, x, y):
        object.__setattr__(self, "x", x)
        object.__setattr__(self, "y", y)
    def __setattr__(self, *args):
        raise TypeError
    def __delattr__(self, *args):
        raise TypeError

অবশ্যই আপনি object.__setattr__(p, "x", 3)একটি Pointউদাহরণটি সংশোধন করতে ব্যবহার করতে পারেন p, তবে আপনার মূল প্রয়োগটি একই সমস্যায় ভুগছে ( tuple.__setattr__(i, "x", 42)একটি Immutableউদাহরণ দিয়ে চেষ্টা করুন )।

আপনি আপনার মূল প্রয়োগে একই কৌশল প্রয়োগ করতে পারেন: পরিত্রাণ পান __getitem__()এবং tuple.__getitem__()আপনার সম্পত্তি কার্যাদি ব্যবহার করুন ।


11
সুপারক্লাস 'ব্যবহার করে কেউ ইচ্ছাকৃতভাবে বস্তুটি সংশোধন করার বিষয়ে আমি চিন্তা করব না __setattr__, কারণ বিন্দুটি নির্বোধ নয়। বিষয়টি হ'ল এটি পরিষ্কার করে দেওয়া উচিত যে এটি পরিবর্তন করা উচিত নয় এবং ভুল করে পরিবর্তনটি রোধ করা উচিত।
zvone

18

আপনি একটি @immutableসাজসজ্জা তৈরি করতে পারেন যা হয় হয় ওভাররাইড করে __setattr__ এবং__slots__ খালি তালিকায় পরিবর্তন করে , তারপরে এটি __init__পদ্ধতিটি সাজান ।

সম্পাদনা: ওপি যেমন উল্লেখ করেছে, __slots__বৈশিষ্ট্য পরিবর্তন করা কেবলমাত্র নতুন বৈশিষ্ট্য তৈরি করতে বাধা দেয় , পরিবর্তন নয়।

সম্পাদনা 2: এখানে একটি বাস্তবায়ন রয়েছে:

সম্পাদনা 3: ব্যবহারটি __slots__এই কোডটিকে বিরতি দেয় কারণ কারণ যদি অবজেক্টের সৃষ্টি থামায়__dict__ । আমি একটি বিকল্প খুঁজছি

সম্পাদনা 4: ঠিক আছে, এটি। এটি একটি হ্যাকিশ, তবে একটি অনুশীলন হিসাবে কাজ করে :-)

class immutable(object):
    def __init__(self, immutable_params):
        self.immutable_params = immutable_params

    def __call__(self, new):
        params = self.immutable_params

        def __set_if_unset__(self, name, value):
            if name in self.__dict__:
                raise Exception("Attribute %s has already been set" % name)

            if not name in params:
                raise Exception("Cannot create atribute %s" % name)

            self.__dict__[name] = value;

        def __new__(cls, *args, **kws):
            cls.__setattr__ = __set_if_unset__

            return super(cls.__class__, cls).__new__(cls, *args, **kws)

        return __new__

class Point(object):
    @immutable(['x', 'y'])
    def __new__(): pass

    def __init__(self, x, y):
        self.x = x
        self.y = y

p = Point(1, 2) 
p.x = 3 # Exception: Attribute x has already been set
p.z = 4 # Exception: Cannot create atribute z

1
সমাধানের বাইরে (শ্রেণি?) সাজসজ্জা বা মেটাক্লাস তৈরি করা ভাল ধারণা, তবে প্রশ্নটি সমাধানটি কী the :)
লেনার্ট রেজেব্রো

3
object.__setattr__()বিরতি এটা stackoverflow.com/questions/4828080/...
JFS

প্রকৃতপক্ষে. আমি সজ্জকারদের উপর একটি অনুশীলন হিসাবে চালিয়েছি।
প্যাওলোভিক্টর

12

হিমায়িত ডেটাগ্লাস ব্যবহার করা

পাইথন ৩.7++ এর জন্য আপনি একটি বিকল্প সহ একটি ডেটা ক্লাস ব্যবহার করতে পারেন যা আপনি যা চান তা করার জন্য খুব অজগর এবং রক্ষণাবেক্ষণের উপায়।frozen=True

এটি দেখতে এমন কিছু লাগবে:

from dataclasses import dataclass

@dataclass(frozen=True)
class Immutable:
    a: Any
    b: Any

যেমন ডেটাচ্লাসের ক্ষেত্রগুলির জন্য টাইপ হিন্টিং প্রয়োজনীয় , আমি মডিউলটি থেকে যেtyping কোনওটি ব্যবহার করেছি ।

নামডটপল ব্যবহার না করার কারণ

পাইথন ৩.7-এর আগে প্রায়শই দেখা যেত নামকরণকৃতরা অপরিবর্তনীয় বস্তু হিসাবে ব্যবহৃত হচ্ছে। এটি বিভিন্ন উপায়ে জটিল হতে পারে, এর __eq__মধ্যে একটি হ'ল নেমডটপলসের মধ্যে থাকা পদ্ধতিটি বস্তুর শ্রেণি বিবেচনা করে না। উদাহরণ স্বরূপ:

from collections import namedtuple

ImmutableTuple = namedtuple("ImmutableTuple", ["a", "b"])
ImmutableTuple2 = namedtuple("ImmutableTuple2", ["a", "c"])

obj1 = ImmutableTuple(a=1, b=2)
obj2 = ImmutableTuple2(a=1, c=2)

obj1 == obj2  # will be True

যেমন আপনি দেখতে পাচ্ছেন, এমনকি যদি তাদের ক্ষেত্রের নামগুলি আলাদা হয় তবে এর প্রকারগুলি obj1এবং obj2পৃথক পৃথক হলেও obj1 == obj2এখনও দেয় True। এর কারণটি __eq__ব্যবহৃত পদ্ধতিটি হল টিউপলের একটি, যা কেবলমাত্র তাদের অবস্থানের ক্ষেত্রের মানগুলির সাথে তুলনা করে। এটি ত্রুটির একটি বিশাল উত্স হতে পারে, বিশেষত যদি আপনি এই ক্লাসগুলি সাবক্লাসিং করে থাকেন।


10

আমি মনে করি না যে এটি সম্পূর্ণরূপে একটি টিউপল বা নেমডটপল ব্যবহার না করেই সম্ভব। যাই হোক না কেন, আপনি যদি ওভাররাইড করেন __setattr__()তবে ব্যবহারকারী সর্বদা object.__setattr__()সরাসরি কল করে এটিকে বাইপাস করতে পারেন । যে কোনও সমাধান নির্ভর করে__setattr__ কাজ না করার গ্যারান্টিযুক্ত।

নীচে আপনি কিছু ধরণের টিপল ব্যবহার না করে আপনি যে নিকটতম পেতে পারেন তা সম্পর্কে:

class Immutable:
    __slots__ = ['a', 'b']
    def __init__(self, a, b):
        object.__setattr__(self, 'a', a)
        object.__setattr__(self, 'b', b)
    def __setattr__(self, *ignored):
        raise NotImplementedError
    __delattr__ = __setattr__

তবে আপনি যথেষ্ট চেষ্টা করলে তা ভেঙে যায়:

>>> t = Immutable(1, 2)
>>> t.a
1
>>> object.__setattr__(t, 'a', 2)
>>> t.a
2

তবে সোভেনের ব্যবহার namedtupleপ্রকৃতপক্ষে অপরিবর্তনীয়।

হালনাগাদ

যেহেতু সিটিতে এটি কীভাবে সঠিকভাবে করা যায় তা জিজ্ঞাসা করার জন্য প্রশ্নটি আপডেট করা হয়েছে, সুতরাং সাইথনে এটি কীভাবে সঠিকভাবে করা যায় সে সম্পর্কে আমার উত্তর:

প্রথম immutable.pyx:

cdef class Immutable:
    cdef object _a, _b

    def __init__(self, a, b):
        self._a = a
        self._b = b

    property a:
        def __get__(self):
            return self._a

    property b:
        def __get__(self):
            return self._b

    def __repr__(self):
        return "<Immutable {0}, {1}>".format(self.a, self.b)

এবং setup.pyএটি সংকলন করতে একটি (কমান্ডটি ব্যবহার করে setup.py build_ext --inplace:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

ext_modules = [Extension("immutable", ["immutable.pyx"])]

setup(
  name = 'Immutable object',
  cmdclass = {'build_ext': build_ext},
  ext_modules = ext_modules
)

তারপরে চেষ্টা করার জন্য:

>>> from immutable import Immutable
>>> p = Immutable(2, 3)
>>> p
<Immutable 2, 3>
>>> p.a = 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: attribute 'a' of 'immutable.Immutable' objects is not writable
>>> object.__setattr__(p, 'a', 1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: attribute 'a' of 'immutable.Immutable' objects is not writable
>>> p.a, p.b
(2, 3)
>>>      

সাইথন কোডের জন্য ধন্যবাদ, সাইথন দুর্দান্ত। পাঠ্যপুস্তকের সাথে জেএফ সেবাস্তিয়ান বাস্তবায়ন আরও সুক্ষ্ম এবং যদিও প্রথমে এসে পৌঁছেছে, তাই তিনি অনুগ্রহ পান।
লেনার্ট রেজেব্রো

5

আমি ওভাররাইড করে অপরিবর্তনীয় ক্লাস করেছি __setattr__এবং কলারটি দিলে সেটটি মঞ্জুরি দিয়েছি __init__:

import inspect
class Immutable(object):
    def __setattr__(self, name, value):
        if inspect.stack()[2][3] != "__init__":
            raise Exception("Can't mutate an Immutable: self.%s = %r" % (name, value))
        object.__setattr__(self, name, value)

এটি এখনও যথেষ্ট নয়, যেহেতু এটি কারওরূপে ___init__বস্তুটি পরিবর্তন করতে দেয় তবে আপনি ধারণাটি পান get


object.__setattr__()বিরতি এটা stackoverflow.com/questions/4828080/...
JFS

3
কলারটি নিশ্চিত করার জন্য স্ট্যাক পরিদর্শন ব্যবহার করা __init__খুব সন্তোষজনক নয়।
জিবি

5

চমৎকার অন্যান্য উত্তরগুলির পাশাপাশি আমি পাইথন ৩.৪ (অথবা সম্ভবত ৩.৩) এর জন্য একটি পদ্ধতি যুক্ত করতে চাই। এই উত্তরটি এই প্রশ্নের বেশ কয়েকটি প্রাইভুউস উত্তরের ভিত্তিতে তৈরি করে।

পাইথন ৩.৪-তে, আপনি শ্রেণিবদ্ধ সদস্য তৈরি করতে সেটার ব্যতীত বৈশিষ্ট্যগুলি ব্যবহার করতে পারেন যা সংশোধন করা যায় না। (পূর্ববর্তী সংস্করণগুলিতে কোনও সেটর ছাড়াই বৈশিষ্ট্যগুলি অর্পণ করা সম্ভব হয়েছিল))

class A:
    __slots__=['_A__a']
    def __init__(self, aValue):
      self.__a=aValue
    @property
    def a(self):
        return self.__a

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

instance=A("constant")
print (instance.a)

যা মুদ্রণ করবে "constant"

তবে কলিংয়ের instance.a=10কারণ হবে:

AttributeError: can't set attribute

ব্যাখ্যা: সেটার ব্যতীত সম্পত্তিগুলি অজগর 3.4 এর খুব সাম্প্রতিক বৈশিষ্ট্য (এবং আমি মনে করি 3.3)। আপনি যদি এই জাতীয় কোনও সম্পত্তি বরাদ্দ করার চেষ্টা করেন তবে একটি ত্রুটি উত্থাপিত হবে। স্লট ব্যবহার করে আমি ঝিল্লিগুলিকে সীমাবদ্ধ রাখি__A_a (যা হয়)__a )।

সমস্যা: নিযুক্ত করা হচ্ছে _A__a এখনও সম্ভব ( instance._A__a=2)। তবে আপনি যদি কোনও ব্যক্তিগত ভেরিয়েবলকে অর্পণ করেন তবে এটি আপনার নিজের দোষ ...

অন্যদের মধ্যে এই উত্তরটি অবশ্য ব্যবহারকে নিরুৎসাহিত করে __slots__। অ্যাট্রিবিউট তৈরি রোধ করতে অন্য উপায় ব্যবহার করা পছন্দনীয় হতে পারে।


propertyপাইথন 2 এও উপলব্ধ (প্রশ্নের নিজের কোডটি দেখুন)। এটি কোনও অপরিবর্তনীয় বস্তু তৈরি করে না, আমার উত্তর থেকে পরীক্ষাগুলি চেষ্টা করুন যেমন instance.b = 1একটি নতুন bবৈশিষ্ট্য তৈরি করে ।
jfs

ঠিক আছে, প্রশ্নটি হল কীভাবে কীভাবে প্রতিরোধ করা যায় A().b = "foo"অর্থাত্ নতুন বৈশিষ্ট্য নির্ধারণের অনুমতি না দেওয়া।
লেনার্ট রেজেব্রো

সেটার ব্যতীত প্রোপারটিস অজগর 3.4 এ একটি ত্রুটি বাড়ায় যদি আপনি সেই সম্পত্তিটিতে অনুদানের চেষ্টা করেন। পূর্ববর্তী সংস্করণগুলিতে সেটারটি অন্তর্নিহিতভাবে উত্পন্ন হয়েছিল।
বার্নহার্ড

@ লেনার্ট: আমার সমাধানটি হ'ল অপরিবর্তনীয় বস্তুর জন্য ব্যবহারের ক্ষেত্রে একটি সাবসেটের উত্তর এবং পূর্ববর্তী উত্তরের সংযোজন। আমি একটি অপরিবর্তনীয় বস্তু চাইবার একটি কারণ হ'ল আমি এটিকে হ্যাশেবল করে তুলতে পারি, যার জন্য আমার সমাধানটি কার্যকর হতে পারে। তবে আপনি সঠিক, এটি কোনও পরিবর্তনযোগ্য বস্তু নয়।
বার্নহার্ড

@ জেএফ-সেবাস্তিয়ান: অ্যাট্রিবিউট তৈরি রোধ করতে স্লটগুলি ব্যবহার করতে আমার উত্তর পরিবর্তন করেছে। অন্যান্য উত্তরের তুলনায় আমার উত্তরে নতুন কী, আমি বিদ্যমান অ্যাট্রিবিউটগুলি পরিবর্তন এড়াতে পাইথন 3.4 এর বৈশিষ্ট্যগুলি ব্যবহার করি। পূর্ববর্তী উত্তরে একই অর্জন করা হলেও, সম্পত্তিগুলির আচরণের পরিবর্তনের কারণে আমার কোডটি সংক্ষিপ্ত।
বার্নহার্ড

5

এখানে একটি মার্জিত সমাধান:

class Immutable(object):
    def __setattr__(self, key, value):
        if not hasattr(self, key):
            super().__setattr__(key, value)
        else:
            raise RuntimeError("Can't modify immutable object's attribute: {}".format(key))

এই শ্রেণি থেকে উত্তরাধিকারী, আপনার ক্ষেত্রগুলি কনস্ট্রাক্টারে আরম্ভ করুন এবং আপনি সমস্ত প্রস্তুত।


1
তবে এই যুক্তির সাহায্যে অবজেক্টটিতে নতুন বৈশিষ্ট্য নির্ধারণ করা সম্ভব
জাভেদ

3

আপনি যদি আচরণের সাথে অবজেক্টগুলিতে আগ্রহী হন, তবে নেমটুপল প্রায় আপনার সমাধান।

নামডটপল ডকুমেন্টেশনের নীচে বর্ণিত হিসাবে , আপনি নেমেডটপল থেকে নিজের ক্লাস নিতে পারেন; এবং তারপরে, আপনি নিজের পছন্দসই আচরণটি যুক্ত করতে পারেন।

উদাহরণস্বরূপ ( ডকুমেন্টেশন থেকে সরাসরি নেওয়া কোড ):

class Point(namedtuple('Point', 'x y')):
    __slots__ = ()
    @property
    def hypot(self):
        return (self.x ** 2 + self.y ** 2) ** 0.5
    def __str__(self):
        return 'Point: x=%6.3f  y=%6.3f  hypot=%6.3f' % (self.x, self.y, self.hypot)

for p in Point(3, 4), Point(14, 5/7):
    print(p)

এর ফলস্বরূপ:

Point: x= 3.000  y= 4.000  hypot= 5.000
Point: x=14.000  y= 0.714  hypot=14.018

এই পদ্ধতিটি পাইথন 3 এবং পাইথন 2.7 উভয়ের জন্যই কাজ করে (আইরন পাইথনের উপরও পরীক্ষিত)।
একমাত্র ক্ষতি হ'ল উত্তরাধিকার গাছটি কিছুটা অদ্ভুত; তবে এটি এমন কিছু নয় যা আপনি সাধারণত খেলেন।


1
পাইথন 3.6+ সরাসরি ব্যবহার করে এটি সমর্থন করেclass Point(typing.NamedTuple):
এলাজার

3

নিম্নোক্ত Immutableশ্রেণীর উত্তরাধিকারসূত্রে প্রাপ্ত ক্লাসগুলি তাদের __init__পদ্ধতি নির্বাহের কাজ শেষ করার পরে তাদের উদাহরণগুলির মতো অপরিবর্তনীয় । যেহেতু এটা বিশুদ্ধ পাইথন আছে, অন্যদের সরু আউট আছে, বেস থেকে mutating বিশেষ পদ্ধতি ব্যবহার করে কেউ বাঁধন কিছুই নেই objectএবংtype , কিন্তু এই দুর্ঘটনা দ্বারা একটি বর্গ / উদাহরণস্বরূপ mutating থেকে স্টপ কারো কাছে যথেষ্ট।

এটি একটি মেটাক্লাস দিয়ে শ্রেণি-নির্মাণ প্রক্রিয়াটিকে হাইজ্যাক করে কাজ করে works

"""Subclasses of class Immutable are immutable after their __init__ has run, in
the sense that all special methods with mutation semantics (in-place operators,
setattr, etc.) are forbidden.

"""  

# Enumerate the mutating special methods
mutation_methods = set()
# Arithmetic methods with in-place operations
iarithmetic = '''add sub mul div mod divmod pow neg pos abs bool invert lshift
                 rshift and xor or floordiv truediv matmul'''.split()
for op in iarithmetic:
    mutation_methods.add('__i%s__' % op)
# Operations on instance components (attributes, items, slices)
for verb in ['set', 'del']:
    for component in '''attr item slice'''.split():
        mutation_methods.add('__%s%s__' % (verb, component))
# Operations on properties
mutation_methods.update(['__set__', '__delete__'])


def checked_call(_self, name, method, *args, **kwargs):
    """Calls special method method(*args, **kw) on self if mutable."""
    self = args[0] if isinstance(_self, object) else _self
    if not getattr(self, '__mutable__', True):
        # self told us it's immutable, so raise an error
        cname= (self if isinstance(self, type) else self.__class__).__name__
        raise TypeError('%s is immutable, %s disallowed' % (cname, name))
    return method(*args, **kwargs)


def method_wrapper(_self, name):
    "Wrap a special method to check for mutability."
    method = getattr(_self, name)
    def wrapper(*args, **kwargs):
        return checked_call(_self, name, method, *args, **kwargs)
    wrapper.__name__ = name
    wrapper.__doc__ = method.__doc__
    return wrapper


def wrap_mutating_methods(_self):
    "Place the wrapper methods on mutative special methods of _self"
    for name in mutation_methods:
        if hasattr(_self, name):
            method = method_wrapper(_self, name)
            type.__setattr__(_self, name, method)


def set_mutability(self, ismutable):
    "Set __mutable__ by using the unprotected __setattr__"
    b = _MetaImmutable if isinstance(self, type) else Immutable
    super(b, self).__setattr__('__mutable__', ismutable)


class _MetaImmutable(type):

    '''The metaclass of Immutable. Wraps __init__ methods via __call__.'''

    def __init__(cls, *args, **kwargs):
        # Make class mutable for wrapping special methods
        set_mutability(cls, True)
        wrap_mutating_methods(cls)
        # Disable mutability
        set_mutability(cls, False)

    def __call__(cls, *args, **kwargs):
        '''Make an immutable instance of cls'''
        self = cls.__new__(cls)
        # Make the instance mutable for initialization
        set_mutability(self, True)
        # Execute cls's custom initialization on this instance
        self.__init__(*args, **kwargs)
        # Disable mutability
        set_mutability(self, False)
        return self

    # Given a class T(metaclass=_MetaImmutable), mutative special methods which
    # already exist on _MetaImmutable (a basic type) cannot be over-ridden
    # programmatically during _MetaImmutable's instantiation of T, because the
    # first place python looks for a method on an object is on the object's
    # __class__, and T.__class__ is _MetaImmutable. The two extant special
    # methods on a basic type are __setattr__ and __delattr__, so those have to
    # be explicitly overridden here.

    def __setattr__(cls, name, value):
        checked_call(cls, '__setattr__', type.__setattr__, cls, name, value)

    def __delattr__(cls, name, value):
        checked_call(cls, '__delattr__', type.__delattr__, cls, name, value)


class Immutable(object):

    """Inherit from this class to make an immutable object.

    __init__ methods of subclasses are executed by _MetaImmutable.__call__,
    which enables mutability for the duration.

    """

    __metaclass__ = _MetaImmutable


class T(int, Immutable):  # Checks it works with multiple inheritance, too.

    "Class for testing immutability semantics"

    def __init__(self, b):
        self.b = b

    @classmethod
    def class_mutation(cls):
        cls.a = 5

    def instance_mutation(self):
        self.c = 1

    def __iadd__(self, o):
        pass

    def not_so_special_mutation(self):
        self +=1

def immutabilityTest(f, name):
    "Call f, which should try to mutate class T or T instance."
    try:
        f()
    except TypeError, e:
        assert 'T is immutable, %s disallowed' % name in e.args
    else:
        raise RuntimeError('Immutability failed!')

immutabilityTest(T.class_mutation, '__setattr__')
immutabilityTest(T(6).instance_mutation, '__setattr__')
immutabilityTest(T(6).not_so_special_mutation, '__iadd__')

2

কিছুক্ষণ আগে আমার এটি দরকার হয়েছিল এবং এর জন্য পাইথন প্যাকেজ তৈরি করার সিদ্ধান্ত নিয়েছি। প্রাথমিক সংস্করণটি এখন পাইপিআইতে রয়েছে:

$ pip install immutable

ব্যবহার করা:

>>> from immutable import ImmutableFactory
>>> MyImmutable = ImmitableFactory.create(prop1=1, prop2=2, prop3=3)
>>> MyImmutable.prop1
1

এখানে সম্পূর্ণ ডক্স: https://github.com/theengineear/immutable

আশা করি এটি সহায়তা করে, এটি যেমন আলোচিত হয়েছে তেমন একটি নেমটুপল গুটিয়ে রাখে তবে ইনস্ট্যান্টেশনকে আরও সহজ করে তোলে।


2

এইভাবে object.__setattr__কাজ করা থামবে না , তবে আমি এখনও এটি দরকারী বলে মনে করেছি:

class A(object):

    def __new__(cls, children, *args, **kwargs):
        self = super(A, cls).__new__(cls)
        self._frozen = False  # allow mutation from here to end of  __init__
        # other stuff you need to do in __new__ goes here
        return self

    def __init__(self, *args, **kwargs):
        super(A, self).__init__()
        self._frozen = True  # prevent future mutation

    def __setattr__(self, name, value):
        # need to special case setting _frozen.
        if name != '_frozen' and self._frozen:
            raise TypeError('Instances are immutable.')
        else:
            super(A, self).__setattr__(name, value)

    def __delattr__(self, name):
        if self._frozen:
            raise TypeError('Instances are immutable.')
        else:
            super(A, self).__delattr__(name)

আপনার __setitem__ব্যবহারের ক্ষেত্রে নির্ভর করে আপনাকে আরও জিনিস (যেমন ) ওভাররাইড করতে হবে ।


আমি এটি দেখার আগে আমি অনুরূপ কিছু নিয়ে এসেছি, তবে ব্যবহার করেছি getattrযাতে আমি এর জন্য একটি ডিফল্ট মান সরবরাহ করতে পারি frozen। যে জিনিস কিছুটা সরলীকৃত। stackoverflow.com/a/22545808/5987
মার্ক

আমি এই পদ্ধতির সেরাটি পছন্দ করি তবে আপনার __new__ওভাররাইডের দরকার নেই । ভিতরে __setattr__কেবলমাত্র শর্তসাপেক্ষে প্রতিস্থাপন করুনif name != '_frozen' and getattr(self, "_frozen", False)
পিট ক্যাসিওপিপি

এছাড়াও, নির্মাণের পরে শ্রেণি হিমায়িত করার প্রয়োজন নেই। আপনি কোনও freeze()ফাংশন সরবরাহ করলে আপনি যেকোন সময় এটি হিম করতে পারেন । এরপরে অবজেক্টটি "একবার নিথর হয়ে যাবে"। অবশেষে, উদ্বিগ্ন object.__setattr__হওয়া নির্বোধ, কারণ "আমরা এখানে সবাই প্রাপ্তবয়স্ক"।
পিট ক্যাসিওপিপি

2

পাইথন ৩.7 হিসাবে, আপনি আপনার শ্রেণিতে @dataclassসজ্জা ব্যবহার করতে পারেন এবং এটি স্ট্রাক্টের মতো অপরিবর্তনীয় হবে! যদিও এটি __hash__()আপনার ক্লাসে কোনও পদ্ধতি যুক্ত করতে পারে বা নাও পারে । উদ্ধৃতি:

হ্যাশ () অন্তর্নির্মিত হ্যাশ () দ্বারা ব্যবহৃত হয়, এবং যখন শব্দগুলি অভিধান এবং সেটগুলির মতো হ্যাশ সংগ্রহের সাথে যুক্ত হয়। একটি হ্যাশ () থাকা ইঙ্গিত দেয় যে শ্রেণীর উদাহরণগুলি অপরিবর্তনীয়। পরিবর্তনশীলতা একটি জটিল সম্পত্তি যে প্রোগ্রামার এর অভিপ্রায়, অস্তিত্ব ও এর আচরণের উপর নির্ভর করে EQ ) (এবং EQ এবং dataclass () প্রসাধক মধ্যে হিমায়িত পতাকার মান।

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

যদি হ্যাশ () সুস্পষ্টভাবে সংজ্ঞায়িত না হয়, বা এটি কোনওটিতে সেট না করা থাকে, তবে ডেটাগ্লাস () একটি অন্তর্ভুক্ত হ্যাশ () পদ্ধতি যুক্ত করতে পারে । প্রস্তাবিত না হলেও, আপনি অনিরাপদ_হ্যাশ = সত্য দিয়ে একটি হ্যাশ () পদ্ধতি তৈরি করতে ডেটাগ্লাসকে () জোর করতে পারেন । এটি হতে পারে যদি আপনার শ্রেণিটি যুক্তিযুক্তভাবে অপরিবর্তনীয় তবে তবে পরিবর্তিত হতে পারে। এটি একটি বিশেষ ব্যবহারের ক্ষেত্রে এবং সাবধানে বিবেচনা করা উচিত।

এখানে উপরে লিঙ্কযুক্ত দস্তাবেজগুলির উদাহরণ:

@dataclass
class InventoryItem:
    '''Class for keeping track of an item in inventory.'''
    name: str
    unit_price: float
    quantity_on_hand: int = 0

    def total_cost(self) -> float:
        return self.unit_price * self.quantity_on_hand

1
আপনি ব্যবহার করতে হবে frozen, অর্থাত্ @dataclass(frozen=True), কিন্তু এটা মূলত ব্লক ব্যবহার __setattr__এবং __delattr__এখানে অন্যান্য উত্তর অধিকাংশ মত। এটি কেবল এটি এমনভাবে করে যা ডেটাক্লাসের অন্যান্য বিকল্পগুলির সাথে সামঞ্জস্যপূর্ণ।
সিএস

2

আপনি setattr ওভাররাইড করতে পারেন এবং ভেরিয়েবল সেট করতে এখনও init ব্যবহার করতে পারেন । আপনি সুপার ক্লাস সেটেট্রার ব্যবহার করবেন । এখানে কোড।

ক্লাস অপরিবর্তনীয়:
    __স্লটস__ = ('এ', 'বি')
    Def __init __ (স্ব, ক, খ):
        সুপার () .__ setattr __ ( 'একটি', ক)
        সুপার () .__ setattr __ ( 'খ', খ)

    Def_____ __ (স্ব):
        রিটার্ন ""। ফর্ম্যাট (স্ব.এ, স্ব.বি)

    Def __setattr __ (স্ব, * উপেক্ষা করা):
        NotImplementedError উত্থাপন করুন

    Def __delattr __ (স্ব, * উপেক্ষা করা):
        NotImplementedError উত্থাপন করুন

বা ঠিক passপরিবর্তেraise NotImplementedError
jonathan.scholbach

এই ক্ষেত্রে __setattr__ এবং __delattr__ এ "পাস" করা মোটেও ভাল ধারণা নয়। এর সহজ কারণটি হ'ল কেউ যদি কোনও ক্ষেত্র / সম্পত্তির জন্য একটি মূল্য নির্ধারণ করে তবে তারা স্বাভাবিকভাবেই প্রত্যাশা করে যে ক্ষেত্রটি পরিবর্তিত হবে। আপনি যদি "ন্যূনতম আশ্চর্য" এর পথটি অনুসরণ করতে চান (যেমনটি আপনার হওয়া উচিত) তবে আপনাকে একটি ত্রুটি বাড়াতে হবে। তবে আমি নিশ্চিত নটমিমিলেটেড এরির সঠিকভাবে উত্থাপিত করে কিনা তা নিশ্চিত নই। আমি "ক্ষেত্র / সম্পত্তি অপরিবর্তনীয়" এর মতো কিছু উত্থাপন করব। ত্রুটি ... আমি মনে করি একটি কাস্টম ব্যতিক্রম নিক্ষেপ করা উচিত।
darlove

1

তৃতীয় পক্ষের attrমডিউলটি এই কার্যকারিতা সরবরাহ করে

সম্পাদনা করুন: পাইথন ৩.7 স্ট্যান্ডলিবের সাথে এই ধারণাটি গ্রহণ করেছে @dataclass

$ pip install attrs
$ python
>>> @attr.s(frozen=True)
... class C(object):
...     x = attr.ib()
>>> i = C(1)
>>> i.x = 2
Traceback (most recent call last):
   ...
attr.exceptions.FrozenInstanceError: can't set attribute

attr__setattr__ডকুমেন্টেশন অনুসারে ওভাররাইড করে হিমশীতল ক্লাস প্রয়োগ করে এবং প্রতিটি তাত্ক্ষণিক সময়ে সামান্য পারফরম্যান্সের প্রভাব ফেলে।

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

attrএছাড়াও একটি সহায়ক প্রদান করে__slots__


1

সুতরাং, আমি পাইথন 3 সম্পর্কিত নিজ নিজ লিখছি:

আমি) ডেটা বর্গ সাজসজ্জার সাহায্যে এবং হিমায়িত = সত্য সেট করে। পাইথনে আমরা অপরিবর্তনীয় বস্তু তৈরি করতে পারি।

এর জন্য ডেটা ক্লাসগুলি থেকে ডেটা ক্লাসটি আমদানি করা প্রয়োজন lib এবং হিমায়িত = সত্য সেট করা দরকার

প্রাক্তন।

ডেটাচ্লাস থেকে ডেটাগ্লাস আমদানি করে

@dataclass(frozen=True)
class Location:
    name: str
    longitude: float = 0.0
    latitude: float = 0.0

ণ; / p &:

l = অবস্থান ("দিল্লি", 112.345, 234.788) l.name 'দিল্লি' l. দৈর্ঘ্য 112.345 l দৈর্ঘ্য 234.788 l.name = "কলকাতা" ডেটাচ্লাস। ফ্রোজেনআইনস্ট্যান্স এরর: 'নাম' ফিল্ডে বরাদ্দ করতে পারবেন না

সূত্র: https://realpython.com/python-data-classes/


0

একটি বিকল্প পন্থা একটি মোড়ক তৈরি করা যা একটি দৃষ্টান্তটিকে অপরিবর্তনীয় করে তোলে।

class Immutable(object):

    def __init__(self, wrapped):
        super(Immutable, self).__init__()
        object.__setattr__(self, '_wrapped', wrapped)

    def __getattribute__(self, item):
        return object.__getattribute__(self, '_wrapped').__getattribute__(item)

    def __setattr__(self, key, value):
        raise ImmutableError('Object {0} is immutable.'.format(self._wrapped))

    __delattr__ = __setattr__

    def __iter__(self):
        return object.__getattribute__(self, '_wrapped').__iter__()

    def next(self):
        return object.__getattribute__(self, '_wrapped').next()

    def __getitem__(self, item):
        return object.__getattribute__(self, '_wrapped').__getitem__(item)

immutable_instance = Immutable(my_instance)

এটি এমন পরিস্থিতিতে কার্যকর যেখানে কেবলমাত্র কয়েকটি উদাহরণ অপরিবর্তনীয় থাকতে হবে (ফাংশন কলগুলির ডিফল্ট যুক্তিগুলির মতো)।

অপরিবর্তনীয় কারখানায় যেমন ব্যবহার করা যায়:

@classmethod
def immutable_factory(cls, *args, **kwargs):
    return Immutable(cls.__init__(*args, **kwargs))

object.__setattr__পাইথনের গতিশীল প্রকৃতির কারণে এছাড়াও সুরক্ষা দেয় , তবে অন্যান্য কৌশলগুলিতে ভ্রান্ত।


0

আমি অ্যালেক্সের মতো একই ধারণাটি ব্যবহার করেছি: একটি মেটা-ক্লাস এবং একটি "আর্ট মার্কার", তবে অতিরিক্ত লেখার সাথে মিলিয়ে __setattr__:

>>> from abc import ABCMeta
>>> _INIT_MARKER = '_@_in_init_@_'
>>> class _ImmutableMeta(ABCMeta):
... 
...     """Meta class to construct Immutable."""
... 
...     def __call__(cls, *args, **kwds):
...         obj = cls.__new__(cls, *args, **kwds)
...         object.__setattr__(obj, _INIT_MARKER, True)
...         cls.__init__(obj, *args, **kwds)
...         object.__delattr__(obj, _INIT_MARKER)
...         return obj
...
>>> def _setattr(self, name, value):
...     if hasattr(self, _INIT_MARKER):
...         object.__setattr__(self, name, value)
...     else:
...         raise AttributeError("Instance of '%s' is immutable."
...                              % self.__class__.__name__)
...
>>> def _delattr(self, name):
...     raise AttributeError("Instance of '%s' is immutable."
...                          % self.__class__.__name__)
...
>>> _im_dict = {
...     '__doc__': "Mix-in class for immutable objects.",
...     '__copy__': lambda self: self,   # self is immutable, so just return it
...     '__setattr__': _setattr,
...     '__delattr__': _delattr}
...
>>> Immutable = _ImmutableMeta('Immutable', (), _im_dict)

দ্রষ্টব্য: পাইথন 2.x এবং 3.x উভয়ের জন্য এটি কাজ করতে আমি সরাসরি মেটা-ক্লাসে কল করছি।

>>> class T1(Immutable):
... 
...     def __init__(self, x=1, y=2):
...         self.x = x
...         self.y = y
...
>>> t1 = T1(y=8)
>>> t1.x, t1.y
(1, 8)
>>> t1.x = 7
AttributeError: Instance of 'T1' is immutable.

এটি স্লটগুলির সাথেও কাজ করে ...:

>>> class T2(Immutable):
... 
...     __slots__ = 's1', 's2'
... 
...     def __init__(self, s1, s2):
...         self.s1 = s1
...         self.s2 = s2
...
>>> t2 = T2('abc', 'xyz')
>>> t2.s1, t2.s2
('abc', 'xyz')
>>> t2.s1 += 'd'
AttributeError: Instance of 'T2' is immutable.

... এবং একাধিক উত্তরাধিকার:

>>> class T3(T1, T2):
... 
...     def __init__(self, x, y, s1, s2):
...         T1.__init__(self, x, y)
...         T2.__init__(self, s1, s2)
...
>>> t3 = T3(12, 4, 'a', 'b')
>>> t3.x, t3.y, t3.s1, t3.s2
(12, 4, 'a', 'b')
>>> t3.y -= 3
AttributeError: Instance of 'T3' is immutable.

নোট, তবে, যে পরিবর্তনীয় বৈশিষ্ট্যগুলি পরিবর্তনীয় হতে পারে:

>>> t3 = T3(12, [4, 7], 'a', 'b')
>>> t3.y.append(5)
>>> t3.y
[4, 7, 5]

0

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

# Initialize lists
a = [1,2,3]
b = [4,5,6]
c = [7,8,9]

l = [a,b]

# We can reassign in a list 
l[0] = c

# But not a tuple
t = (a,b)
#t[0] = c -> Throws exception
# But elements can be modified
t[0][1] = 4
t
([1, 4, 3], [4, 5, 6])
# Fix it back
t[0][1] = 2

li = ImmutableObject(l)
li
[[1, 2, 3], [4, 5, 6]]
# Can't assign
#li[0] = c will fail
# Can reference
li[0]
[1, 2, 3]
# But immutability conferred on returned object too
#li[0][1] = 4 will throw an exception

# Full solution should wrap all the comparison e.g. decorators.
# Also, you'd usually want to add a hash function, i didn't put
# an interface for that.

class ImmutableObject(object):
    def __init__(self, inobj):
        self._inited = False
        self._inobj = inobj
        self._inited = True

    def __repr__(self):
        return self._inobj.__repr__()

    def __str__(self):
        return self._inobj.__str__()

    def __getitem__(self, key):
        return ImmutableObject(self._inobj.__getitem__(key))

    def __iter__(self):
        return self._inobj.__iter__()

    def __setitem__(self, key, value):
        raise AttributeError, 'Object is read-only'

    def __getattr__(self, key):
        x = getattr(self._inobj, key)
        if callable(x):
              return x
        else:
              return ImmutableObject(x)

    def __hash__(self):
        return self._inobj.__hash__()

    def __eq__(self, second):
        return self._inobj.__eq__(second)

    def __setattr__(self, attr, value):
        if attr not in  ['_inobj', '_inited'] and self._inited == True:
            raise AttributeError, 'Object is read-only'
        object.__setattr__(self, attr, value)

0

আপনি মাত্র init এর চূড়ান্ত বিবৃতিতে setAttr ওভাররাইড করতে পারেন। আপনি নির্মাণ করতে পারেন তবে পরিবর্তন করতে পারবেন না। অবশ্যই আপনি এখনও usint অবজেক্ট দ্বারা ওভাররাইড করতে পারেন। setAttr তবে অনুশীলনে বেশিরভাগ ভাষায় প্রতিবিম্বের কিছু ফর্ম থাকে তাই সর্বদা একটি ফাঁস বিমূর্ততা। অপ্রচলতা ক্লায়েন্টদের কোনও ঘটনার সাথে চুক্তি লঙ্ঘন থেকে রোধ করার বিষয়ে আরও বেশি। আমি ব্যবহার করি:

=============================

দেওয়া মূল সমাধানটি ভুল ছিল, এখান থেকে সমাধানটি ব্যবহার করে মন্তব্যের ভিত্তিতে এটি আপডেট করা হয়েছিল

মূল সমাধানটি আকর্ষণীয় উপায়ে ভুল, সুতরাং এটি নীচে অন্তর্ভুক্ত করা হয়েছে।

===============================

class ImmutablePair(object):

    __initialised = False # a class level variable that should always stay false.
    def __init__(self, a, b):
        try :
            self.a = a
            self.b = b
        finally:
            self.__initialised = True #an instance level variable

    def __setattr__(self, key, value):
        if self.__initialised:
            self._raise_error()
        else :
            super(ImmutablePair, self).__setattr__(key, value)

    def _raise_error(self, *args, **kw):
        raise NotImplementedError("Attempted To Modify Immutable Object")

if __name__ == "__main__":

    immutable_object = ImmutablePair(1,2)

    print immutable_object.a
    print immutable_object.b

    try :
        immutable_object.a = 3
    except Exception as e:
        print e

    print immutable_object.a
    print immutable_object.b

আউটপুট:

1
2
Attempted To Modify Immutable Object
1
2

======================================

আসল বাস্তবায়ন:

মন্তব্যে এটি সঠিকভাবে উল্লেখ করা হয়েছিল, সঠিকভাবে, এটি বাস্তবে কাজ করে না, কারণ এটি শ্রেণি সেট্যাট্রিক পদ্ধতির উপর নজর রাখার কারণে এটি একাধিক অবজেক্টের সৃষ্টি আটকাচ্ছে, যার অর্থ একটি দ্বিতীয়টি স্ব.আ = উইল হিসাবে তৈরি করা যাবে না দ্বিতীয় সূচনাতে ব্যর্থ।

class ImmutablePair(object):

    def __init__(self, a, b):
        self.a = a
        self.b = b
        ImmutablePair.__setattr__ = self._raise_error

    def _raise_error(self, *args, **kw):
        raise NotImplementedError("Attempted To Modify Immutable Object")

1
এটি কাজ করবে না: আপনি ক্লাসে পদ্ধতিটি ওভাররাইড করছেন , সুতরাং আপনি দ্বিতীয় উদাহরণ তৈরি করার চেষ্টা করার সাথে সাথেই NotImplementedError পেয়ে যাবেন।
23

1
আপনি যদি এই পদ্ধতির অনুসরণ করতে চান তবে নোট করুন যে রানটাইমের সময় বিশেষ পদ্ধতিগুলিকে ওভাররাইড করা কঠিন: এটির একটি দম্পতি কাজের জন্য stackoverflow.com/a/16426447/137635 দেখুন ।
23

0

নীচের মৌলিক সমাধানটি নিম্নলিখিত দৃশ্যের সমাধান করে:

  • __init__() যথারীতি অ্যাট্রিবিউটগুলি অ্যাক্সেস করে লেখা যেতে পারে।
  • এর পরে কেবলমাত্র বৈশিষ্ট্য পরিবর্তনের জন্য ওবিজেইসিটি হিমশীতল :

ধারণাটি হ'ল __setattr__পদ্ধতিটিকে ওভাররাইড করে এবং প্রতিবার যখন বস্তুর হিমায়িত স্থিতি পরিবর্তন করা হয় তখন এর বাস্তবায়ন প্রতিস্থাপন করা হয়।

সুতরাং আমাদের কিছু পদ্ধতি প্রয়োজন ( _freeze) যা সেই দুটি বাস্তবায়ন সঞ্চয় করে এবং যখন অনুরোধ করা হয় তখন তাদের মধ্যে পরিবর্তন করে।

এই প্রক্রিয়াটি ব্যবহারকারী শ্রেণীর অভ্যন্তরে প্রয়োগ করা যেতে পারে বা Freezerনীচের মত একটি বিশেষ শ্রেণীর উত্তরাধিকার সূত্রে প্রাপ্ত হতে পারে :

class Freezer:
    def _freeze(self, do_freeze=True):
        def raise_sa(*args):            
            raise AttributeError("Attributes are frozen and can not be changed!")
        super().__setattr__('_active_setattr', (super().__setattr__, raise_sa)[do_freeze])

    def __setattr__(self, key, value):        
        return self._active_setattr(key, value)

class A(Freezer):    
    def __init__(self):
        self._freeze(False)
        self.x = 10
        self._freeze()

0

ঠিক যেমন একটি dict

আমার একটি ওপেন সোর্স লাইব্রেরি রয়েছে যেখানে আমি কার্যকরী উপায়ে জিনিসগুলি করি তাই এক অপরিবর্তনীয় বস্তুর চারপাশে ডেটা সরিয়ে নেওয়া সহায়ক। তবে ক্লায়েন্টের সাথে যোগাযোগ করার জন্য আমার ডেটা অবজেক্টটি রূপান্তর করতে চাই না। সুতরাং, আমি এটি নিয়ে হাজির হয়েছি - এটি আপনাকে অব্যবস্থাপনা + কিছু সহায়ক পদ্ধতিগুলির মতো একটি ডিক দেয়

সম্পত্তি আপডেট করা এবং মুছে ফেলার সীমাবদ্ধ করার প্রাথমিক প্রয়োগের জন্য তার উত্তরে সোভেন মারনাচের প্রতি ক্রেডিট ।

import json 
# ^^ optional - If you don't care if it prints like a dict
# then rip this and __str__ and __repr__ out

class Immutable(object):

    def __init__(self, **kwargs):
        """Sets all values once given
        whatever is passed in kwargs
        """
        for k,v in kwargs.items():
            object.__setattr__(self, k, v)

    def __setattr__(self, *args):
        """Disables setting attributes via
        item.prop = val or item['prop'] = val
        """
        raise TypeError('Immutable objects cannot have properties set after init')

    def __delattr__(self, *args):
        """Disables deleting properties"""
        raise TypeError('Immutable objects cannot have properties deleted')

    def __getitem__(self, item):
        """Allows for dict like access of properties
        val = item['prop']
        """
        return self.__dict__[item]

    def __repr__(self):
        """Print to repl in a dict like fashion"""
        return self.pprint()

    def __str__(self):
        """Convert to a str in a dict like fashion"""
        return self.pprint()

    def __eq__(self, other):
        """Supports equality operator
        immutable({'a': 2}) == immutable({'a': 2})"""
        if other is None:
            return False
        return self.dict() == other.dict()

    def keys(self):
        """Paired with __getitem__ supports **unpacking
        new = { **item, **other }
        """
        return self.__dict__.keys()

    def get(self, *args, **kwargs):
        """Allows for dict like property access
        item.get('prop')
        """
        return self.__dict__.get(*args, **kwargs)

    def pprint(self):
        """Helper method used for printing that
        formats in a dict like way
        """
        return json.dumps(self,
            default=lambda o: o.__dict__,
            sort_keys=True,
            indent=4)

    def dict(self):
        """Helper method for getting the raw dict value
        of the immutable object"""
        return self.__dict__

সহায়ক পদ্ধতি

def update(obj, **kwargs):
    """Returns a new instance of the given object with
    all key/val in kwargs set on it
    """
    return immutable({
        **obj,
        **kwargs
    })

def immutable(obj):
    return Immutable(**obj)

উদাহরণ

obj = immutable({
    'alpha': 1,
    'beta': 2,
    'dalet': 4
})

obj.alpha # 1
obj['alpha'] # 1
obj.get('beta') # 2

del obj['alpha'] # TypeError
obj.alpha = 2 # TypeError

new_obj = update(obj, alpha=10)

new_obj is not obj # True
new_obj.get('alpha') == 10 # True
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.