আমি কীভাবে "স্ব। এক্স = এক্স এড়াতে পারি? self.y = y; __init__ এ স্ব.জ = জেড "প্যাটার্ন?


170

আমি নিদর্শন দেখতে

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

প্রায়শই প্রায়শই অনেক বেশি পরামিতি থাকে। এই জাতীয় ক্লান্তিকর পুনরাবৃত্তি এড়াতে কি কোনও ভাল উপায় আছে? ক্লাসের namedtupleপরিবর্তে উত্তরাধিকারী হওয়া উচিত ?


31
সমস্ত গ্রহণযোগ্যতা খারাপ হয় না। মনে রাখবেন পাইথনের ক্লাস মডেলটিতে উদাহরণ বৈশিষ্টগুলির সুস্পষ্ট সংজ্ঞা অন্তর্ভুক্ত নয়, সুতরাং এই কার্যাদি স্ব-ডকুমেন্টিং সমতুল্য।
চ্যানার

4
@ চেপারার: ​​ঠিক আছে, সুস্পষ্ট সংজ্ঞাটির প্রয়োজন নেই । আপনি __slots__যদিও এই উদ্দেশ্যে ব্যবহার করতে পারেন ; এটি হালকা অপ্রচলিত (মেমরি সঞ্চয় পেতে আরও ভার্বোজ), তবে আমি নামটি টাইপ করলে সম্পূর্ণ নতুন বৈশিষ্ট্যটিকে স্বয়ংক্রিয়ভাবে বিভক্ত করার ঝুঁকি এড়ানো বেশিরভাগ ক্ষেত্রেই আমার পছন্দ হয়।
শ্যাডোর্যাঞ্জার

2
যে কোনও ভাল সম্পাদকের টেম্পলেট থাকবে। আপনি টাইপ করুন ini <shortcut> x, y, z): <shortcut>এবং আপনার কাজ শেষ হয়েছে।
গেরেনুক

3
নামবিহীন টিপলস দুর্দান্ত, যদি আপনি একটি অপরিবর্তনীয় মান বস্তু চান। আপনি যদি নিয়মিত, পরিবর্তনীয় শ্রেণি চান তবে আপনি সেগুলি ব্যবহার করতে পারবেন না।
রিমকো গ্রিলিচ

4
"করবেন না" একটি ভাল বিকল্প, উপলভ্য যে কোনও বিকল্প পদ্ধতি স্বাক্ষরকে (এবং এইভাবে সম্ভাব্যভাবে পুরো ইন্টারফেসটি) মেরে ফেলবে। তদ্ব্যতীত, যদি আপনার ক্লাসগুলির একটি অসহনীয় পরিমাণ ক্ষেত্র আরম্ভ করতে হয় তবে আপনি সেগুলি বিভক্ত করার বিষয়টি বিবেচনা করতে পারেন।
Kroltan

উত্তর:


87

সম্পাদনা করুন: আপনার কাছে অজগর থাকলে 3.7+ মাত্র ব্যবহার করুন ডেটা চশমা করুন

একটি সাজসজ্জার সমাধান যা স্বাক্ষর রাখে:

import decorator
import inspect
import sys


@decorator.decorator
def simple_init(func, self, *args, **kws):
    """
    @simple_init
    def __init__(self,a,b,...,z)
        dosomething()

    behaves like

    def __init__(self,a,b,...,z)
        self.a = a
        self.b = b
        ...
        self.z = z
        dosomething()
    """

    #init_argumentnames_without_self = ['a','b',...,'z']
    if sys.version_info.major == 2:
        init_argumentnames_without_self = inspect.getargspec(func).args[1:]
    else:
        init_argumentnames_without_self = tuple(inspect.signature(func).parameters.keys())[1:]

    positional_values = args
    keyword_values_in_correct_order = tuple(kws[key] for key in init_argumentnames_without_self if key in kws)
    attribute_values = positional_values + keyword_values_in_correct_order

    for attribute_name,attribute_value in zip(init_argumentnames_without_self,attribute_values):
        setattr(self,attribute_name,attribute_value)

    # call the original __init__
    func(self, *args, **kws)


class Test():
    @simple_init
    def __init__(self,a,b,c,d=4):
        print(self.a,self.b,self.c,self.d)

#prints 1 3 2 4
t = Test(1,c=2,b=3)
#keeps signature
#prints ['self', 'a', 'b', 'c', 'd']
if sys.version_info.major == 2:
    print(inspect.getargspec(Test.__init__).args)
else:
    print(inspect.signature(Test.__init__))

2
উত্তম উত্তর, তবে পাইথন 2.7 নিয়ে কাজ করবে না: নাsignature
ম্যাক্সবি

3
@ অ্যালেক্স-এ "ডেকোরেটর.ডেকোরেটর" সাজসজ্জারটি স্বয়ংক্রিয়ভাবে ফাংশনটি
গুটিয়ে রাখে

4
আমি এটিকে ভালবাসি বা ঘৃণা করি সে সম্পর্কে আমি বেশ ছিঁড়ে পড়েছি। আমি স্বাক্ষর সংরক্ষণ প্রশংসা করি।
কাইল স্ট্র্যান্ড 21

14
"... স্পষ্ট বর্ণিত চেয়ে ভাল। জটিল চেয়ে সহজ।" "-পাইথনের জেন
জ্যাক স্টাউট

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

108

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


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

তবে, যেহেতু আপনি জিজ্ঞাসা করছেন এটি কীভাবে করা যেতে পারে (এবং এটি করা উচিত কিনা) তবে একটি সমাধান হ'ল:

class A:
    def __init__(self, **kwargs):
        for key in kwargs:
          setattr(self, key, kwargs[key])

a = A(l=1, d=2)
a.l # will return 1
a.d # will return 2

16
ভাল উত্তর +1 ... যদিও self.__dict__.update(kwargs)সামান্য কিছুটা অজগর হতে পারে
জোড়ান বিসলে

44
এই পদ্ধতির সাথে সমস্যাটি হ'ল আর্গুমেন্টগুলি A.__init__আসলে কী প্রত্যাশা করে তার কোনও রেকর্ড নেই এবং ভুল টাইপ করা যুক্তির নাম অনুসন্ধানের জন্য কোনও ত্রুটি নেই।
ম্যাক্সবি

7
@ জোড়ানবিসলে kwargsআপনি এসকিউএল ইঞ্জেকশন আক্রমণের সমতুল্য পাতাগুলির সাথে অন্ধভাবে উদাহরণের অভিধানটি আপডেট করছেন । যদি আপনার অবজেক্টের একটি নামকরণের পদ্ধতি থাকে my_methodএবং আপনি my_methodকনস্ট্রাক্টরের নামে একটি আর্গুমেন্ট পাস করেন, তবে update()অভিধান, আপনি কেবল পদ্ধতিটি ওভাররোট করেন।
পেড্রো

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

3
@ পেড্রো গ্রুজকি এবং জোড়ানবিজলির সিনট্যাক্সের মধ্যে কি কোনও অর্থগত পার্থক্য রয়েছে?
অঙ্কুরিত

29

অন্যরা যেমন উল্লেখ করেছেন, পুনরাবৃত্তিটি খারাপ নয়, তবে কিছু ক্ষেত্রে এই ধরণের ইস্যুতে একটি নেমটুপল দুর্দান্ত মানানসই হতে পারে। এটি স্থানীয় () বা কাওয়ার্গগুলি ব্যবহার করা এড়ানো যায় যা সাধারণত খারাপ ধারণা।

from collections import namedtuple
# declare a new object type with three properties; x y z
# the first arg of namedtuple is a typename
# the second arg is comma-separated or space-separated property names
XYZ = namedtuple("XYZ", "x, y, z")

# create an object of type XYZ. properties are in order
abc = XYZ("one", "two", 3)
print abc.x
print abc.y
print abc.z

আমি এটির জন্য সীমিত ব্যবহার খুঁজে পেয়েছি তবে আপনি অন্য কোনও বস্তুর মতো নামকরণের উত্তরাধিকারী হতে পারেন (উদাহরণস্বরূপ অবিরত):

class MySuperXYZ(XYZ):
    """ I add a helper function which returns the original properties """
    def properties(self):
        return self.x, self.y, self.z

abc2 = MySuperXYZ(4, "five", "six")
print abc2.x
print abc2.y
print abc2.z
print abc2.properties()

5
এই হয় , তাই আপনার, tuples propertiesপদ্ধতি হিসাবে শুধু লেখা যেতে পারে return tuple(self)যা প্রায়ই রক্ষণীয় হলে ভবিষ্যতে আরও ক্ষেত্র namedtuple সংজ্ঞা যোগ করা হয়।
PaulMcG

1
এছাড়াও, আপনার নামযুক্ত দ্বার ঘোষণার স্ট্রিংয়ের ক্ষেত্রের নামগুলির মধ্যে কমা প্রয়োজন হয় না, XYZ = namedtuple("XYZ", "x y z")ঠিক পাশাপাশি কাজ করে।
PaulMcG

ধন্যবাদ @ পলম্যাকগুইরে। আমি উত্তরাধিকার এবং এটির মধ্যে দুরত্বের ধরণটি দেখানোর জন্য একটি খুব সহজ অ্যাড-অনের কথা ভাবার চেষ্টা করছিলাম। আপনি 100% সঠিক এবং এটি অন্যান্য উত্তরাধিকারসূত্রে প্রাপ্ত বস্তুর সাথেও দুর্দান্ত শর্টহ্যান্ড! আমি উল্লেখ করি না ক্ষেত্রের নামগুলি কমা বা স্থান পৃথক হতে পারে - আমি অভ্যাস থেকে সিএসভি পছন্দ করি
একটি ছোট শেল স্ক্রিপ্ট

1
আমি প্রায়শই namedtupleএই সঠিক উদ্দেশ্যে ব্যবহার করি , বিশেষত গাণিতিক কোডে যেখানে কোনও ফাংশন অত্যন্ত প্যারামিট্রিসড হতে পারে এবং সহগের একটি গুচ্ছ থাকতে পারে যা কেবলমাত্র একসাথে বোঝায়।
অবধি

সমস্যাটি namedtupleহ'ল এগুলি কেবল পঠনযোগ্য। আপনি না করতে পারেন abc.x += 1বা এর মতো কিছু।
হ্যামস্টারজিন

29

সুস্পষ্ট বর্ণনামূলক চেয়ে ভাল ... সুতরাং আপনি এটিকে আরও সংক্ষিপ্ত করে তুলতে পারবেন তা নিশ্চিত:

def __init__(self,a,b,c):
    for k,v in locals().items():
        if k != "self":
             setattr(self,k,v)

আরও ভাল প্রশ্ন আপনি করা উচিত?

... এটি বলেছিল যে আপনি যদি নামকরণযুক্ত টিপল চান তবে আমি একটি নেমডটল ব্যবহার করার পরামর্শ দেব (মনে রাখবেন টিপলগুলির সাথে কিছু শর্ত যুক্ত থাকে) ... সম্ভবত আপনি আদেশ আদেশ বা এমনকি কেবল একটি ডিক চান ...


তারপরে অবজেক্টটির চক্রীয় আবর্জনা সংগ্রহের প্রয়োজন হবে কারণ এটির একটি বৈশিষ্ট্য হিসাবে এটি রয়েছে
জন লা রুই

3
@ বার্নি (নাকি এটি বেমি?), কখনও কখনও কে আর নিং শক্ত হয়
বিড়াল

4
কিছুটা আরও দক্ষ পরীক্ষার if k != "self":জন্য if v is not self:, স্ট্রিং তুলনা না করে সস্তা পরিচয় পরীক্ষায় পরিবর্তিত হতে পারে । আমি মনে করি __init__নির্মাণের পরে প্রযুক্তিগতভাবে দ্বিতীয়বার ডাকা যেতে পারে এবং selfপরবর্তী যুক্তি হিসাবে পাস করা হয়েছিল, তবে আমি কী ভাবতে চাই না যে এটি কী ধরণের দৈত্য তা করবে। :-)
শ্যাডোর্যাঞ্জার

এটা একটা ফাংশন যার ফেরত মান লাগে মধ্যে তৈরি করা যেতে পারে locals: set_fields_from_locals(locals())। তারপরে এটি আর বেশি যাদুকর ডেকোরেটর ভিত্তিক সমাধানগুলির চেয়ে বেশি নয়।
লিই

20

gruszczyএর উত্তরে প্রসারিত করতে , আমি একটি প্যাটার্ন ব্যবহার করেছি:

class X:
    x = None
    y = None
    z = None
    def __init__(self, **kwargs):
        for (k, v) in kwargs.items():
            if hasattr(self, k):
                setattr(self, k, v)
            else:
                raise TypeError('Unknown keyword argument: {:s}'.format(k))

আমি এই পদ্ধতিটি পছন্দ করি কারণ এটি:

  • পুনরাবৃত্তি এড়ানো
  • কোনও অবজেক্ট তৈরি করার সময় টাইপগুলির বিরুদ্ধে প্রতিরোধী
  • সাবক্লাসিংয়ের সাথে ভাল কাজ করে (কেবল পারে super().__init(...) )
  • এর পরিবর্তে শ্রেণিক স্তরের (যেখানে তারা অন্তর্ভুক্ত) বৈশিষ্ট্যগুলির ডকুমেন্টেশনের অনুমতি দেয় X.__init__

পাইথন ৩.6 এর আগে এটি বৈশিষ্ট্য নির্ধারণ করা হয়েছে এমন ক্রমের উপরে কোনও নিয়ন্ত্রণ দেয় না, যদি কিছু বৈশিষ্ট্যগুলি সেটারগুলির সাথে বৈশিষ্ট্যযুক্ত হয় যা অন্যান্য বৈশিষ্ট্যগুলিতে অ্যাক্সেস করে।

এটি সম্ভবত কিছুটা উন্নতি হতে পারে, তবে আমি নিজের কোডের একমাত্র ব্যবহারকারী তাই আমি কোনও ধরণের ইনপুট স্যানিটেশন সম্পর্কে উদ্বিগ্ন নই। সম্ভবত একটি AttributeErrorআরও উপযুক্ত হবে।


10

আপনি এছাড়াও করতে পারেন:

locs = locals()
for arg in inspect.getargspec(self.__init__)[0][1:]:
    setattr(self, arg, locs[arg])

অবশ্যই, আপনাকে inspectমডিউলটি আমদানি করতে হবে ।


8

এটি কোনও অতিরিক্ত আমদানি ছাড়াই সমাধান।

সহায়ক ফাংশন

একটি ছোট সহায়ক ফাংশন এটিকে আরও সুবিধাজনক এবং পুনরায় ব্যবহারযোগ্য করে তোলে:

def auto_init(local_name_space):
    """Set instance attributes from arguments.
    """
    self = local_name_space.pop('self')
    for name, value in local_name_space.items():
        setattr(self, name, value)

আবেদন

আপনার সাথে এটি কল করতে হবে locals():

class A:
    def __init__(self, x, y, z):
        auto_init(locals())

পরীক্ষা

a = A(1, 2, 3)
print(a.__dict__)

আউটপুট:

{'y': 2, 'z': 3, 'x': 1}

পরিবর্তন না করেই locals()

আপনি যদি locals()এই সংস্করণটি ব্যবহার করতে চান না :

def auto_init(local_name_space):
    """Set instance attributes from arguments.
    """
    for name, value in local_name_space.items():
        if name != 'self': 
            setattr(local_name_space['self'], name, value)

docs.python.org/2/library/funifications.html# লোকালগুলিকে locals() সংশোধন করা উচিত নয় (এটি আপনার ক্ষেত্রে selfকলিং ফাংশনটির সুযোগটি অপসারণ করে দোভাষীকে প্রভাবিত করতে পারে )
ম্যাক্সবি

@ ম্যাক্সবি আপনি যে দস্তাবেজগুলি থেকে উদ্ধৃত করেছেন: ... পরিবর্তনগুলি দোভাষী দ্বারা ব্যবহৃত স্থানীয় এবং বিনামূল্যে ভেরিয়েবলের মানগুলিকে প্রভাবিত করতে পারে না selfএখনও পাওয়া যায় __init__
মাইক

রাইট, পাঠক স্থানীয় ভেরিয়েবল প্রভাবিত করার আশা, কিন্তু এটা বা থাকতে পারি না , পরিস্থিতিতে একটি সংখ্যার উপর নির্ভর করে। মুল বক্তব্যটি এটি ইউবি।
ম্যাক্সবি

উদ্ধৃতি: "এই অভিধানের বিষয়বস্তুগুলি সংশোধন করা উচিত নয়"
ম্যাক্সবি

@ ম্যাক্সবি আমি এমন একটি সংস্করণ যুক্ত করেছি যা স্থানীয়দের () পরিবর্তন করে না।
মাইক মুলার

7

একটি আকর্ষণীয় লাইব্রেরি যা এটি পরিচালনা করে (এবং প্রচুর অন্যান্য বয়লারপ্লেট এড়ায়) এটি অ্যাটর্স । আপনার উদাহরণ, উদাহরণস্বরূপ, এটি হ্রাস করা যেতে পারে (অনুমান করা হয় শ্রেণি বলা হয় MyClass):

import attr

@attr.s
class MyClass:
    x = attr.ib()
    y = attr.ib()
    z = attr.ib()

এমনকি আপনার __init__আর কোনও পদ্ধতির দরকার নেই, যদি না এটি অন্যান্য জিনিসও করে। গ্লাইফ লেফকোভিটসের একটি চমৎকার ভূমিকা এখানে ।


attrরিডানড্যান্ট তৈরির কার্যকারিতাটি কত ডিগ্রি dataclasses?
অঙ্কিত

1
@gerrit এটি অ্যাটর্স প্যাকেজের ডকুমেন্টেশনে আলোচনা করা হয়েছে । টিবিএইচ, পার্থক্যগুলি এত বড় বলে মনে হয় না।
আইভো মার্চিয়র্স

5

আমার 0.02 $ এটি জোড়ান বিসলে উত্তরের খুব কাছাকাছি, তবে আরও মার্জিত:

def __init__(self, a, b, c, d, e, f):
    vars(self).update((k, v) for k, v in locals().items() if v is not self)

অতিরিক্তভাবে, মাইক মোলারের উত্তর (আমার স্বাদে সেরা) এই কৌশলটি দিয়ে হ্রাস করা যেতে পারে:

def auto_init(ns):
    self = ns.pop('self')
    vars(self).update(ns)

এবং আপনার auto_init(locals())কাছ থেকে জাস্ট কল call__init__


1
docs.python.org/2/library/funitions.html# লোকালগুলিকে locals() সংশোধন করা উচিত নয় (
অপরিজ্ঞাত

4

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


4

পাইথন ৩.7 এর পরে

পাইথন ৩.7-এ, আপনি (আব) মডিউলটি dataclassথেকে পাওয়া সজ্জা ব্যবহার করতে পারেন dataclasses। ডকুমেন্টেশন থেকে:

এই মডিউলটি একটি সাজসজ্জার সরবরাহ করে এবং স্বয়ংক্রিয়ভাবে উত্পন্ন বিশেষ পদ্ধতি যেমন __init__()এবং এর জন্য সংযুক্ত করে__repr__() ব্যবহারকারী-সংজ্ঞায়িত শ্রেণিতে । এটি মূলত পিইপি 557 তে বর্ণিত হয়েছিল।

এই উত্পন্ন পদ্ধতিতে ব্যবহারের জন্য সদস্য ভেরিয়েবলগুলি পিইপি 526 প্রকারের টিকা ব্যবহার করে সংজ্ঞায়িত করা হয়। উদাহরণস্বরূপ এই কোড:

@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

অন্যান্য জিনিসগুলির মধ্যে __init__()এমন একটি যুক্ত হবে যা দেখতে দেখতে:

def __init__(self, name: str, unit_price: float, quantity_on_hand: int=0):
      self.name = name
      self.unit_price = unit_price
      self.quantity_on_hand = quantity_on_hand

নোট করুন যে এই পদ্ধতিটি স্বয়ংক্রিয়ভাবে ক্লাসে যুক্ত হয়েছে: এটি উপরে প্রদর্শিত ইনভেন্টরি আইটেম সংজ্ঞাটিতে সরাসরি নির্দিষ্ট করা হয়নি।

যদি আপনার শ্রেণিটি বড় এবং জটিল হয় তবে এটি ব্যবহার করা অনুচিত হতে পারে dataclass। আমি পাইথন ৩..0.০ প্রকাশের দিনে এটি লিখছি, সুতরাং ব্যবহারের ধরণগুলি এখনও ভালভাবে প্রতিষ্ঠিত হয়নি।

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