سؤال

هل هناك أي تمييز ذي معنى بين:

class A(object):
    foo = 5   # some default value

ضد.

class B(object):
    def __init__(self, foo=5):
        self.foo = foo

إذا كنت تقوم بإنشاء الكثير من المثيلات، فهل هناك أي اختلاف في الأداء أو متطلبات المساحة للنمطين؟عندما تقرأ الكود، هل تعتبر أن معنى النمطين مختلفان بشكل كبير؟

هل كانت مفيدة؟

المحلول

وراء اعتبارات الأداء، وهناك كبير <م> الدلالي الفرق. في حالة سمة فئة، هناك كائن واحد فقط المشار إليها. في المقام السمة بين مجموعة المعرضة للمثيل، يمكن أن يكون هناك كائنات متعددة المشار إليها. على سبيل المثال

>>> class A: foo = []
>>> a, b = A(), A()
>>> a.foo.append(5)
>>> b.foo
[5]
>>> class A:
...  def __init__(self): self.foo = []
>>> a, b = A(), A()
>>> a.foo.append(5)
>>> b.foo    
[]

نصائح أخرى

والفرق هو أن السمة على الطبقة المشتركة من قبل جميع الحالات. السمة على مثيل هي فريدة من نوعها لذلك المثيل.

إذا القادمة من C ++، والصفات على الطبقة هي أشبه المتغيرات عضو ثابت.

هنا هو جيد جدا بريد, ، وتلخيصها على النحو التالي.

class Bar(object):
    ## No need for dot syntax
    class_var = 1

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

## Need dot syntax as we've left scope of class namespace
Bar.class_var
## 1
foo = MyClass(2)

## Finds i_var in foo's instance namespace
foo.i_var
## 2

## Doesn't find class_var in instance namespace…
## So look's in class namespace (Bar.__dict__)
foo.class_var
## 1

وفي شكل مرئي

enter image description here

تعيين سمة الطبقة

  • إذا تم تعيين سمة فئة عن طريق الوصول إلى الفئة، فسوف تتجاوز قيمة جميع الحالات

    foo = Bar(2)
    foo.class_var
    ## 1
    Bar.class_var = 2
    foo.class_var
    ## 2
    
  • إذا تم تعيين متغير فئة عن طريق الوصول إلى مثيل، فسوف يتجاوز القيمة فقط لهذا المثال.يؤدي هذا بشكل أساسي إلى تجاوز متغير الفئة وتحويله إلى متغير مثيل متاح، بشكل حدسي، فقط لهذا المثال.

    foo = Bar(2)
    foo.class_var
    ## 1
    foo.class_var = 2
    foo.class_var
    ## 2
    Bar.class_var
    ## 1
    

متى تستخدم سمة الفئة؟

  • تخزين الثوابت.نظرًا لأنه يمكن الوصول إلى سمات الفئة كسمات للفئة نفسها، فغالبًا ما يكون من الجيد استخدامها لتخزين الثوابت الخاصة بالفئة على مستوى الفئة

    class Circle(object):
         pi = 3.14159
    
         def __init__(self, radius):
              self.radius = radius   
        def area(self):
             return Circle.pi * self.radius * self.radius
    
    Circle.pi
    ## 3.14159
    c = Circle(10)
    c.pi
    ## 3.14159
    c.area()
    ## 314.159
    
  • تحديد القيم الافتراضية.كمثال تافه، قد نقوم بإنشاء قائمة محدودة (أي قائمة يمكن أن تحتوي فقط على عدد معين من العناصر أو أقل) ونختار أن يكون الحد الأقصى الافتراضي هو 10 عناصر

    class MyClass(object):
        limit = 10
    
        def __init__(self):
            self.data = []
        def item(self, i):
            return self.data[i]
    
        def add(self, e):
            if len(self.data) >= self.limit:
                raise Exception("Too many elements")
            self.data.append(e)
    
     MyClass.limit
     ## 10
    

ولأن الناس في التعليقات هنا وفي سؤالين أخرى وضعت DUPS كل يبدو أن الخلط حول هذا في نفس الطريق، وأعتقد أنه يستحق مضيفا إجابة إضافية على رأس <لأ href = "HTTPS: // ستاكوفيرفلوو كوم / أ / 207128/908494 "> أليكس كوفنتري .

والحقيقة أن أليكس تحديد قيمة من نوع قابلة للتغيير، مثل قائمة، ليس له علاقة سواء الأشياء المشتركة أو لا شيء. يمكننا أن نرى هذا مع وظيفة id أو المشغل is:

>>> class A: foo = object()
>>> a, b = A(), A()
>>> a.foo is b.foo
True
>>> class A:
...     def __init__(self): self.foo = object()
>>> a, b = A(), A()
>>> a.foo is b.foo
False

<الفرعية> (إذا كنت أتساءل لماذا كنت object() بدلا من، مثلا، 5، وهذا لتجنب الوقوع في قضيتين أخريين كلها التي لا نريد ان نصل الى هنا، لسببين مختلفين، بشكل منفصل تماما يمكن 5s -created في نهاية الأمر نفس مثيل 5 عدد، ولكن تماما object()s التي تم إنشاؤها على حدة لا تستطيع ذلك.)


وهكذا، لماذا هو أن a.foo.append(5) في المثال اليكس يؤثر b.foo، ولكن a.foo = 5 في بلدي على سبيل المثال لا؟ حسنا، في محاولة a.foo = 5 في المثال اليكس، ولاحظ أنه لا تؤثر b.foo هناك <م> إما .

وa.foo = 5 هو مجرد جعل a.foo إلى اسم ل5. أن لا يؤثر b.foo، أو أي اسم آخر لقيمة القديمة التي a.foo يستخدم للإشارة إلى. * انها صعبة بعض الشيء لأننا بصدد إنشاء سمة المثيل الذي يخفي سمة فئة **، ولكن بمجرد الحصول على ذلك، لا شيء تعقيدا يحدث هنا.


ونأمل انها الآن واضحا لماذا يستخدم أليكس قائمة: حقيقة أنك يمكن أن يتحور لائحة يعني أنه من الأسهل أن تبين أن اثنين من المتغيرات أسم نفس القائمة، وتعني أيضا أنها أكثر أهمية في التعليمات البرمجية من واقع الحياة لمعرفة ما إذا كان لديك قائمتين أو اسمين لنفس القائمة.


<الفرعية> * الارتباك بالنسبة للأشخاص القادمين من لغة مثل C ++ هو أنه في بيثون، لا يتم تخزين القيم في المتغيرات. قيم تعيش حالا في قيمة الأراضي، من تلقاء نفسها، والمتغيرات هي مجرد أسماء للقيم، وتعيين فقط بإنشاء اسم جديد للقيمة. إذا كان يساعد، والتفكير في كل متغير بيثون باعتبارها shared_ptr<T> بدلا من T.

<الفرعية> ** بعض الناس من الاستفادة من هذا باستخدام سمة فئة ب "القيمة الافتراضية" لمثيل السمة أن الحالات قد أو قد لا تعيينها. هذا يمكن أن يكون مفيدا في بعض الحالات، ولكن يمكن أيضا أن يكون مربكا، لذا كن حذرا معها.

هناك حالة أخرى.

سمات الفئة والمثيل هي واصف.

# -*- encoding: utf-8 -*-


class RevealAccess(object):
    def __init__(self, initval=None, name='var'):
        self.val = initval
        self.name = name

    def __get__(self, obj, objtype):
        return self.val


class Base(object):
    attr_1 = RevealAccess(10, 'var "x"')

    def __init__(self):
        self.attr_2 = RevealAccess(10, 'var "x"')


def main():
    b = Base()
    print("Access to class attribute, return: ", Base.attr_1)
    print("Access to instance attribute, return: ", b.attr_2)

if __name__ == '__main__':
    main()

أعلاه سوف يخرج:

('Access to class attribute, return: ', 10)
('Access to instance attribute, return: ', <__main__.RevealAccess object at 0x10184eb50>)

نفس النوع من الوصول إلى المثيل من خلال الفصل أو المثيل يُرجع نتائج مختلفة!

ووجدت في تعريف c.PyObject_GenericGetAttr، وعظيم بريد.

يشرح

إذا تم العثور على السمة في قاموس الفئات التي تتكون منها.الكائنات MRO، ثم تحقق لمعرفة ما إذا كانت السمة التي يتم البحث عنها تشير إلى واصف البيانات (وهو ليس أكثر من فئة تطبق كلاً من __get__ و ال __set__ طُرق).إذا كان الأمر كذلك، قم بحل مشكلة البحث عن السمة عن طريق استدعاء __get__ طريقة واصف البيانات (السطور 28-33).

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top