পাইথনে, আমি কীভাবে ইঙ্গিত করব যে আমি কোনও পদ্ধতিকে ওভাররাইড করছি?


173

জাভাতে, উদাহরণস্বরূপ, @Overrideটীকাগুলি কেবল একটি ওভাররাইডের সংকলন-সময় পরীক্ষা করে না তবে দুর্দান্ত স্ব-ডকুমেন্টিং কোড তৈরি করে for

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


13
অন্য কথায়, আপনি কখনই ইঙ্গিত করেন না যে আপনি কোনও পদ্ধতির উপর নজর রেখেছেন? নিজেই তা বের করার জন্য পাঠকের কাছে রেখে যাবেন?
ব্লু

2
হ্যাঁ, আমি জানি এটি একটি সঙ্কলিত ভাষা থেকে আসা একটি ত্রুটিযুক্ত প্রবণ পরিস্থিতির মতো মনে হচ্ছে, তবে আপনাকে কেবল এটি গ্রহণ করতে হবে। বাস্তবে আমি এটি খুব বেশি সমস্যার হিসাবে পাইনি (আমার ক্ষেত্রে রুবি, পাইথন নয়, একই ধারণা)
এড এস

অবশ্যই, সম্পন্ন। ট্রিপটিচের উত্তর এবং এমকোর্পেলার উত্তর দুটিই সহজ I
ব্লু

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

উত্তর:


206

এর ভিত্তিতে এবং fwc: s জবাব আমি একটি পাইপ ইনস্টলযোগ্য প্যাকেজ তৈরি করেছি https://github.com/mkorpela/overrides

সময়ে সময়ে আমি এই প্রশ্নের দিকে তাকিয়ে এখানেই শেষ করি। আমাদের কোড বেসে একই ত্রুটি দেখার পরে (আবার) প্রধানত এটি ঘটে: "ইন্টারফেস" এ একটি পদ্ধতির নাম পরিবর্তন করার সময় কেউ "ইন্টারফেস" প্রয়োগকারী শ্রেণীর কিছু ভুলে গেছে ..

আচ্ছা পাইথন জাভা নয় তবে পাইথনের শক্তি রয়েছে - এবং অন্তর্নিহিতের চেয়ে সুস্পষ্ট - এবং আসল বিশ্বে এমন বাস্তব কংক্রিট রয়েছে যেখানে এই জিনিসটি আমাকে সহায়তা করেছিল।

সুতরাং এখানে ওভাররাইডস ডেকরেটারের স্কেচ is এটি পরীক্ষা করবে যে প্যারামিটার হিসাবে প্রদত্ত শ্রেণীর পদ্ধতিটি যেমন সাজানো হচ্ছে তেমন একই পদ্ধতি (বা কিছু) নাম রয়েছে।

আপনি যদি আরও ভাল সমাধানের কথা ভাবতে পারেন তবে এখানে পোস্ট করুন!

def overrides(interface_class):
    def overrider(method):
        assert(method.__name__ in dir(interface_class))
        return method
    return overrider

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

class MySuperInterface(object):
    def my_method(self):
        print 'hello world!'


class ConcreteImplementer(MySuperInterface):
    @overrides(MySuperInterface)
    def my_method(self):
        print 'hello kitty!'

এবং আপনি যদি কোনও ত্রুটিযুক্ত সংস্করণ করেন তবে এটি শ্রেণি লোডিংয়ের সময় একটি দৃ error়তা ত্রুটি বাড়িয়ে তুলবে:

class ConcreteFaultyImplementer(MySuperInterface):
    @overrides(MySuperInterface)
    def your_method(self):
        print 'bye bye!'

>> AssertionError!!!!!!!

17
অসাধারণ. এটি প্রথমবার চেষ্টা করার পরে একটি ভুল বানান ত্রুটি পেয়েছে। যশ।
ক্রিস্টোফার ব্রুনস

7
এমএফবুটনার: প্রতিবার পদ্ধতিটি কার্যকর করার সময় এটি বলা হয় না - কেবল যখন পদ্ধতিটি তৈরি করা হয়।
mkorpela

3
এটি ডক স্ট্রিংয়ের জন্যও দুর্দান্ত! overridesওভাররাইডিং পদ্ধতির নিজস্ব একটি না থাকলে ওভাররাইড পদ্ধতিটির ডকস্ট্রিংটি অনুলিপি করতে পারে।
লেটমাইক

5
@ এমকোর্পেলা, হে আপনার এই কোডটি অজগর ডিফল্ট লাইব সিস্টেমে থাকা উচিত। আপনি এটি পাইপ সিস্টেমে রাখেন না কেন? : পি

5
@ এমকোর্পেলা: ওহ এবং আমি এই প্যাকেজটি ঘিরে পাইথন কোর বিকাশকারীদের অবহিত করার পরামর্শ দিচ্ছি, তারা মূল পাইথন সিস্টেমে ওভারাইড ডেকরেটার যুক্ত করার বিষয়ে বিবেচনা করতে চাইতে পারে। :)

30

এখানে এমন একটি বাস্তবায়ন যা ইন্টারফেস_ক্লাস নামের নির্দিষ্টকরণের প্রয়োজন হয় না।

import inspect
import re

def overrides(method):
    # actually can't do this because a method is really just a function while inside a class def'n  
    #assert(inspect.ismethod(method))

    stack = inspect.stack()
    base_classes = re.search(r'class.+\((.+)\)\s*\:', stack[2][4][0]).group(1)

    # handle multiple inheritance
    base_classes = [s.strip() for s in base_classes.split(',')]
    if not base_classes:
        raise ValueError('overrides decorator: unable to determine base class') 

    # stack[0]=overrides, stack[1]=inside class def'n, stack[2]=outside class def'n
    derived_class_locals = stack[2][0].f_locals

    # replace each class name in base_classes with the actual class type
    for i, base_class in enumerate(base_classes):

        if '.' not in base_class:
            base_classes[i] = derived_class_locals[base_class]

        else:
            components = base_class.split('.')

            # obj is either a module or a class
            obj = derived_class_locals[components[0]]

            for c in components[1:]:
                assert(inspect.ismodule(obj) or inspect.isclass(obj))
                obj = getattr(obj, c)

            base_classes[i] = obj


    assert( any( hasattr(cls, method.__name__) for cls in base_classes ) )
    return method

2
কিছুটা মায়াবী তবে সাধারণ ব্যবহারকে অনেক সহজ করে তোলে। আপনি ব্যবহারের উদাহরণ অন্তর্ভুক্ত করতে পারেন?
ব্লু

এই ডেকোরেটরটি ব্যবহার করার জন্য গড় এবং সবচেয়ে খারাপ ক্ষেত্রে কতগুলি ব্যয় হতে পারে, সম্ভবত @ ক্লাসমেডথ বা @ প্রোপার্টি এর মতো বিল্ট-ইন সাজসজ্জার সাথে তুলনা হিসাবে প্রকাশ করা হয়েছে?
লার্হাম 1

4
@ লারহাম 1 এই ডেকরেটারটি একবার কার্যকর করা হয়, যখন ক্লাসের সংজ্ঞাটি প্রতিটি কলটিতে নয়, বিশ্লেষণ করা হয়। সুতরাং প্রোগ্রামের রানটাইমের তুলনায় এটি কার্যকর করার ব্যয় অপ্রাসঙ্গিক।
আবগান

এটি পাইথন 3.6 তে অনেক সুন্দর হবে পিইপি 487 ধন্যবাদ ।
নিল জি

আরও ভাল ত্রুটির বার্তাটি পেতে: বেস (ক্লাসের ক্লাসগুলির জন্য যে কোনও (hasattr (cls, পদ্ধতি .__ নাম__)) চাপুন, 'ওভারাইডেন পদ্ধতি "{}" বেস শ্রেণিতে পাওয়া যায় নি।' বিন্যাসে (পদ্ধতি .__ নাম__)
ইভান

14

আপনি যদি কেবল ডকুমেন্টেশনের প্রয়োজনে এটি চান তবে আপনি নিজের ওভাররাইড ডেকরেটারকে সংজ্ঞায়িত করতে পারেন:

def override(f):
    return f


class MyClass (BaseClass):

    @override
    def method(self):
        pass

এটি চক্ষু-ক্যান্ডি ছাড়া কিছুই নয়, যদি না আপনি এমনভাবে ওভাররাইড (চ) তৈরি করেন যা আসলে ওভাররাইডের জন্য পরীক্ষা করা হয়।

তবে, এটি পাইথন, এটি জাভা মতো লেখেন কেন?


2
overrideসাজসজ্জারের মাধ্যমে পরিদর্শনের মাধ্যমে কেউ আসল বৈধতা যুক্ত করতে পারে ।
এরিক কাপলুন

70
তবে, এটি পাইথন, এটি জাভা মতো লেখেন কেন? কারণ জাভাতে কিছু ধারণা ভাল এবং অন্যান্য ভাষায় প্রসারিত?
পাইওটর ডব্রোগস্ট

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

4
কারণ এটি একটি ভাল ধারণা। বিভিন্ন ভাষায় বিভিন্ন বৈশিষ্ট্যের বৈশিষ্ট্য থাকার বিষয়টি কোনও যুক্তি নয় - হয় পক্ষে বা বিপক্ষে।
sfkleach

6

পাইথন জাভা নয়। কম্পাইল-টাইম চেকিংয়ের মতো আসলে কিছুই নেই।

আমি মনে করি ডক্ট্রিংয়ে একটি মন্তব্য যথেষ্ট। এটি আপনার পদ্ধতির যে কোনও ব্যবহারকারীর টাইপ করতে help(obj.method)এবং দেখতে পাবে যে পদ্ধতিটি ওভাররাইড।

আপনি স্পষ্টভাবে এর সাথে একটি ইন্টারফেসও প্রসারিত করতে পারেন class Foo(Interface), যা ব্যবহারকারীদের help(Interface.method)আপনার পদ্ধতিটি সরবরাহ করার উদ্দেশ্যে কার্যকারিতা সম্পর্কে ধারণা পেতে টাইপ করতে দেবে ।


57
@Overrideজাভার আসল বিষয়টি নথিভুক্ত করা নয় - আপনি যখন কোনও পদ্ধতিকে ওভাররাইড করার পরিকল্পনা করেছিলেন তখন এটি ভুল ধরা পড়েছিল, তবে একটি নতুনটির সংজ্ঞা দেওয়া শেষ করেছিল (উদাহরণস্বরূপ আপনি জাভাতে কোনও নামের ভুল বানান করেছেন বলে; জাভাতেও এটি ঘটতে পারে কারণ আপনি ব্যবহার করেছেন ভুল স্বাক্ষর, কিন্তু পাইথন এটি কোনও সমস্যা নয় - তবে বানান ভুল এখনও রয়েছে)।
পাভেল মিনায়েভ

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

2
@ পাভেলমিনায়েভ ভুল এর মূল পয়েন্টগুলির মধ্যে একটি @Overrideহ'ল ডকুমেন্টেশন সংকলন সময় পরীক্ষা করা ছাড়াও।
সিয়ামি

6
@ সিয়ামি আমি মনে করি ডকুমেন্টেশনের জন্য একটি সহায়তা দুর্দান্ত, তবে আমি যে সমস্ত অফিসিয়াল জাভা ডকুমেন্টেশন দেখছি তাতে তারা কেবল সংকলনের সময় চেকের গুরুত্ব নির্দেশ করে। দয়া করে আপনার দাবিটি প্রমাণ করুন যে পাভেল "ভুল"।
অ্যান্ড্রু মেলঞ্জার

5

@Mkorpela দুর্দান্ত উত্তরে উন্নতি করা , এর একটি সংস্করণ এখানে

আরও সুনির্দিষ্ট চেক, নামকরণ এবং উত্থাপিত ত্রুটিযুক্ত বস্তু

def overrides(interface_class):
    """
    Function override annotation.
    Corollary to @abc.abstractmethod where the override is not of an
    abstractmethod.
    Modified from answer https://stackoverflow.com/a/8313042/471376
    """
    def confirm_override(method):
        if method.__name__ not in dir(interface_class):
            raise NotImplementedError('function "%s" is an @override but that'
                                      ' function is not implemented in base'
                                      ' class %s'
                                      % (method.__name__,
                                         interface_class)
                                      )

        def func():
            pass

        attr = getattr(interface_class, method.__name__)
        if type(attr) is not type(func):
            raise NotImplementedError('function "%s" is an @override'
                                      ' but that is implemented as type %s'
                                      ' in base class %s, expected implemented'
                                      ' type %s'
                                      % (method.__name__,
                                         type(attr),
                                         interface_class,
                                         type(func))
                                      )
        return method
    return confirm_override


বাস্তবে এটি দেখতে কেমন দেখাচ্ছে:

NotImplementedError" বেস শ্রেণিতে প্রয়োগ করা হয়নি "

class A(object):
    # ERROR: `a` is not a implemented!
    pass

class B(A):
    @overrides(A)
    def a(self):
        pass

আরও বর্ণনামূলক NotImplementedErrorত্রুটি ফলাফল

function "a" is an @override but that function is not implemented in base class <class '__main__.A'>

পূর্ণ স্ট্যাক

Traceback (most recent call last):
  
  File "C:/Users/user1/project.py", line 135, in <module>
    class B(A):
  File "C:/Users/user1/project.py", line 136, in B
    @overrides(A)
  File "C:/Users/user1/project.py", line 110, in confirm_override
    interface_class)
NotImplementedError: function "a" is an @override but that function is not implemented in base class <class '__main__.A'>


NotImplementedError" প্রত্যাশিত বাস্তবায়িত প্রকার "

class A(object):
    # ERROR: `a` is not a function!
    a = ''

class B(A):
    @overrides(A)
    def a(self):
        pass

আরও বর্ণনামূলক NotImplementedErrorত্রুটি ফলাফল

function "a" is an @override but that is implemented as type <class 'str'> in base class <class '__main__.A'>, expected implemented type <class 'function'>

পূর্ণ স্ট্যাক

Traceback (most recent call last):
  
  File "C:/Users/user1/project.py", line 135, in <module>
    class B(A):
  File "C:/Users/user1/project.py", line 136, in B
    @overrides(A)
  File "C:/Users/user1/project.py", line 125, in confirm_override
    type(func))
NotImplementedError: function "a" is an @override but that is implemented as type <class 'str'> in base class <class '__main__.A'>, expected implemented type <class 'function'>




@ এমকোরপেলা উত্তর সম্পর্কে দুর্দান্ত জিনিস হ'ল চেকটি কোনও আরম্ভের পর্যায়ে ঘটে। চেকটি "রান" করার দরকার নেই। পূর্ববর্তী উদাহরণগুলি উল্লেখ করে class Bকখনই আরম্ভ করা হয় না ( B()) তবুও NotImplementedErrorতবুও উত্থাপিত হবে। এর অর্থ overridesত্রুটিগুলি দ্রুত ধরা পড়ে।


হে! এটি আকর্ষণীয় দেখায়। আপনি আমার আইপ্রোমাইজ প্রকল্পে একটি টান অনুরোধ করছেন বিবেচনা করতে পারেন? আমি একটি উত্তর যুক্ত করেছি।
নিল জি

@ নীলজি আমি আইপ্রোমাইজ প্রকল্পটি কাঁটাচামু করে কিছুটা কোড করেছিলাম । দেখে মনে হচ্ছে আপনি এটি অবশ্যই মূলত এর মধ্যে প্রয়োগ করেছেন overrides.py। আমি নিশ্চিত আর কি আমি উল্লেখযোগ্যভাবে থেকে ব্যতিক্রম ধরনের পরিবর্তন করার ব্যতীত উন্নত করতে পারেন নই TypeErrorকরতে NotImplementedError
জেমস থমাসমুন 1979

হে! ধন্যবাদ, আমার কাছে চেক নেই যে ওভাররাইড করা বস্তুর আসলে টাইপ আছে types.MethodType। আপনার উত্তরে এটি একটি ভাল ধারণা ছিল।
নিল জি

2

অন্যরা যেমন জাভা থেকে আলাদাভাবে বলেছে যে সেখানে ওভারাইড ট্যাগ নেই তবে উপরে আপনি নিজের সাজসজ্জা ব্যবহার করে নিজের তৈরি করতে পারেন তবে আমি অভ্যন্তরীণ ডিক ব্যবহার না করে গেট্যাটরিব () গ্লোবাল পদ্ধতি ব্যবহার করার পরামর্শ দিচ্ছি যাতে আপনি নীচের মতো কিছু পান:

def Override(superClass):
    def method(func)
        getattr(superClass,method.__name__)
    return method

আপনি যদি নিজের ইচ্ছামতো গেটআটার () ধরতে চান তবে নিজের ত্রুটি বাড়িয়ে তুলতে পারেন তবে আমি মনে করি এই ক্ষেত্রে গেটআটার পদ্ধতি আরও ভাল।

এছাড়াও এটি ক্লাসের পদ্ধতি এবং বৈকল্পিক সহ সমস্ত শ্রেণীর সাথে আবদ্ধ সমস্ত আইটেম ধরে


2

উপর @ mkorpela এর মহান উত্তর নির্ভর করে, আমি একটি অনুরূপ প্যাকেজ লিখিত করেছি ( ipromise pypi GitHub ) যে আরো অনেক চেক করে:

ধরুন Aথেকে উত্তরাধিকারী Bএবং C, Bথেকে ইনহেরিটC

মডিউল আইপ্রোমাইস পরীক্ষা করে যে:

  • তাহলে A.fওভাররাইড B.f, B.fথাকা আবশ্যক, এবং Aথেকে উত্তরাধিকারী হবে B। (এটি ওভাররাইড প্যাকেজ থেকে প্রাপ্ত চেক)।

  • আপনার প্যাটার্নটি A.fঘোষণা করে না যে এটি ওভাররাইড করে B.f, যা তখন ঘোষণা করে যে এটি ওভাররাইড করে C.fAবলা উচিত C.fযেহেতু Bএ পদ্ধতিটি ওভাররাইড করা বন্ধ করার সিদ্ধান্ত নিতে পারে সেহেতু এটি ওভাররাইড করে এবং এর ফলে ডাউন স্ট্রিম আপডেটগুলি হওয়া উচিত নয়।

  • আপনার প্যাটার্নটি A.fঘোষণা করে না যে এটি ওভাররাইড করে C.fতবে B.fএটির ওভাররাইড ঘোষণা করে না।

  • আপনার প্যাটার্নটি A.fঘোষণা করে না যে এটি ওভাররাইড করে C.f, কিন্তু B.fঘোষণা করে যে এটি কিছু থেকে ওভাররাইড করে D.f

এটিতে একটি বিমূর্ত পদ্ধতি প্রয়োগ এবং চিহ্নিতকরণের জন্য বিভিন্ন বৈশিষ্ট্য রয়েছে has


0

শুনা খুব সহজ এবং জাভা ক্লাসগুলির সাথে জাইথনের অধীনে কাজ করছে:

class MyClass(SomeJavaClass):
     def __init__(self):
         setattr(self, "name_of_method_to_override", __method_override__)

     def __method_override__(self, some_args):
         some_thing_to_do()

0

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

উত্স কোডের জন্য দেখুন: https://github.com/fireuser909/override

এই অলঙ্করণকারী কেবলমাত্র সেই ক্লাসগুলির জন্য কাজ করে যা ওভাররাইডের উদাহরণ ver ওভাররাইডস মেটা তবে আপনার শ্রেণি যদি কাস্টম মেটাক্লাসের উদাহরণ হয় তবে ওভাররাইড ডিকোরিটারের সাথে সামঞ্জস্যপূর্ণ একটি মেটাক্লাস তৈরি করতে create_custom_overrides_meta ফাংশনটি ব্যবহার করুন। পরীক্ষাগুলির জন্য, ওভাররাইড .__ init__ মডিউলটি চালান।


0

পাইথন ২.6+ এবং পাইথন ৩.২+ তে আপনি এটি করতে পারেন ( আসলে এটি অনুকরণ করুন , পাইথন ফাংশন ওভারলোডিং সমর্থন করে না এবং শিশু শ্রেণিটি পিতামাতার পদ্ধতিটি স্বয়ংক্রিয়ভাবে ওভাররাইড করে)। আমরা এর জন্য সজ্জিত ব্যবহার করতে পারি। তবে প্রথমে লক্ষ করুন যে পাইথন @decoratorsএবং জাভা @Annotationsসম্পূর্ণ আলাদা জিনিস। পূর্ববর্তীটি হ'ল কংক্রিট কোড সহ একটি মোড়ক এবং পরে একটি সংকলকটির পতাকা।

এই জন্য, প্রথমে না pip install multipledispatch

from multipledispatch import dispatch as Override
# using alias 'Override' just to give you some feel :)

class A:
    def foo(self):
        print('foo in A')

    # More methods here


class B(A):
    @Override()
    def foo(self):
        print('foo in B')
    
    @Override(int)
    def foo(self,a):
        print('foo in B; arg =',a)
        
    @Override(str,float)
    def foo(self,a,b):
        print('foo in B; arg =',(a,b))
        
a=A()
b=B()
a.foo()
b.foo()
b.foo(4)
b.foo('Wheee',3.14)

আউটপুট:

foo in A
foo in B
foo in B; arg = 4
foo in B; arg = ('Wheee', 3.14)

নোট করুন যে আপনাকে অবশ্যই প্রথম বন্ধনী সহ ডেকরেটার ব্যবহার করতে হবে

একটি বিষয় মনে রাখবেন যেহেতু পাইথনের সরাসরি ওভারলোডিং ফাংশন নেই, সুতরাং ক্লাস বি ক্লাস এ থেকে উত্তরাধিকারসূত্রে নাও আসে তবে fooআপনার ওভাররাইড ব্যবহার করার প্রয়োজনের তুলনায় আপনার সমস্ত প্রয়োজনের প্রয়োজন হয় (যদিও 'ওভারলোড' নামটি ব্যবহার করা হবে তবে চেহারাটি দেখতে পাবেন) সেক্ষেত্রে ভাল)

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