จุดประสงค์ของคลาสภายในของ python คืออะไร?


100

คลาสภายใน / ซ้อนของ Python ทำให้ฉันสับสน มีบางสิ่งที่ไม่สามารถทำได้หากไม่มีพวกเขา? ถ้าเป็นเช่นนั้นสิ่งนั้นคืออะไร?

คำตอบ:


87

อ้างจากhttp://www.geekinterview.com/question_details/64739 :

ข้อดีของชั้นใน:

  • การจัดกลุ่มคลาสแบบลอจิคัล : ถ้าคลาสมีประโยชน์กับคลาสอื่นเพียงคลาสเดียวก็มีเหตุผลที่จะฝังคลาสนั้นลงในคลาสนั้น การซ้อน "คลาสตัวช่วย" ดังกล่าวทำให้แพ็กเกจมีความคล่องตัวมากขึ้น
  • การห่อหุ้มที่เพิ่มขึ้น : พิจารณาคลาสระดับบนสุดสองคลาส A และ B โดยที่ B ต้องการเข้าถึงสมาชิกของ A ที่จะประกาศให้เป็นส่วนตัว การซ่อนคลาส B ภายในสมาชิกคลาส AA สามารถประกาศเป็นส่วนตัวและ B สามารถเข้าถึงได้ นอกจากนี้ B เองยังสามารถซ่อนตัวจากโลกภายนอกได้
  • โค้ดที่อ่านง่ายและบำรุงรักษาได้มากขึ้น : การซ้อนคลาสเล็ก ๆ ภายในคลาสระดับบนสุดจะทำให้โค้ดใกล้กับที่ที่ใช้

ประโยชน์หลักคือการจัดระเบียบ สิ่งใดก็ตามที่สามารถทำได้ด้วยคลาสภายในสามารถทำได้หากไม่มีพวกเขา


52
แน่นอนว่าอาร์กิวเมนต์ encapsulation ใช้ไม่ได้กับ Python
Bobince

32
จุดแรกใช้ไม่ได้กับ Python เช่นกัน คุณสามารถกำหนดคลาสได้มากในไฟล์โมดูลเดียวเท่าที่คุณต้องการดังนั้นการรวมคลาสไว้ด้วยกันและองค์กรแพ็กเกจจะไม่ได้รับผลกระทบเช่นกัน ประเด็นสุดท้ายเป็นเรื่องส่วนตัวมากและฉันไม่เชื่อว่ามันถูกต้อง ในระยะสั้นฉันไม่พบข้อโต้แย้งใด ๆ ที่สนับสนุนการใช้คลาสภายในใน Python ในคำตอบนี้
Chris Arndt

17
อย่างไรก็ตามสิ่งเหล่านี้เป็นเหตุผลที่ใช้คลาสภายในในการเขียนโปรแกรม คุณแค่พยายามตอบคำถามคู่แข่ง คำตอบนี้ที่เพื่อนคนนี้ให้มานั้นมั่นคง
Inversus

17
@ Inversus: ฉันไม่เห็นด้วย นี่ไม่ใช่คำตอบ แต่เป็นคำพูดเพิ่มเติมจากคำตอบของคนอื่นเกี่ยวกับภาษาอื่น (เช่น Java) ลดลงและฉันหวังว่าคนอื่นจะทำเช่นเดียวกัน
Kevin

6
ฉันเห็นด้วยกับคำตอบนี้และไม่เห็นด้วยกับการคัดค้าน แม้ว่าคลาสที่ซ้อนกันไม่ใช่คลาสภายในของ Java แต่ก็มีประโยชน์ วัตถุประสงค์ของคลาสที่ซ้อนกันคือการจัดระเบียบ คุณวางคลาสหนึ่งไว้ใต้เนมสเปซของอีกคลาสได้อย่างมีประสิทธิภาพ เมื่อมันสมเหตุสมผลที่จะทำเช่นนั้น Pythonic นี่คือ : "Namespaces เป็นแนวคิดที่ยอดเยี่ยมอย่างหนึ่ง - มาทำสิ่งเหล่านั้นให้มากขึ้น!" ตัวอย่างเช่นพิจารณาDataLoaderคลาสที่สามารถทำให้เกิดCacheMissข้อยกเว้นได้ การซ้อนข้อยกเว้นภายใต้คลาสหลักDataLoader.CacheMissหมายความว่าคุณสามารถนำเข้าได้DataLoaderแต่ยังคงใช้ข้อยกเว้น
cbarrick

51

มีบางสิ่งที่ไม่สามารถทำได้หากไม่มีพวกเขา?

ไม่พวกเขาเทียบเท่ากับการกำหนดชั้นเรียนตามปกติที่ระดับบนสุดแล้วคัดลอกการอ้างอิงไปยังชั้นนอก

ฉันไม่คิดว่ามีเหตุผลพิเศษใด ๆ ที่ชั้นเรียนที่ซ้อนกันได้รับอนุญาตนอกจากนี้ไม่มีเหตุผลใดที่จะ 'ไม่อนุญาต' อย่างชัดเจนเช่นกัน

หากคุณกำลังมองหาคลาสที่มีอยู่ภายในวงจรชีวิตของอ็อบเจ็กต์ภายนอก / เจ้าของและมักจะมีการอ้างอิงถึงอินสแตนซ์ของคลาสภายนอก - คลาสภายในเช่นเดียวกับ Java - คลาสที่ซ้อนกันของ Python จะไม่ใช่สิ่งนั้น แต่คุณสามารถแฮ็กบางอย่างเช่นสิ่งนั้น:

import weakref, new

class innerclass(object):
    """Descriptor for making inner classes.

    Adds a property 'owner' to the inner class, pointing to the outer
    owner instance.
    """

    # Use a weakref dict to memoise previous results so that
    # instance.Inner() always returns the same inner classobj.
    #
    def __init__(self, inner):
        self.inner= inner
        self.instances= weakref.WeakKeyDictionary()

    # Not thread-safe - consider adding a lock.
    #
    def __get__(self, instance, _):
        if instance is None:
            return self.inner
        if instance not in self.instances:
            self.instances[instance]= new.classobj(
                self.inner.__name__, (self.inner,), {'owner': instance}
            )
        return self.instances[instance]


# Using an inner class
#
class Outer(object):
    @innerclass
    class Inner(object):
        def __repr__(self):
            return '<%s.%s inner object of %r>' % (
                self.owner.__class__.__name__,
                self.__class__.__name__,
                self.owner
            )

>>> o1= Outer()
>>> o2= Outer()
>>> i1= o1.Inner()
>>> i1
<Outer.Inner inner object of <__main__.Outer object at 0x7fb2cd62de90>>
>>> isinstance(i1, Outer.Inner)
True
>>> isinstance(i1, o1.Inner)
True
>>> isinstance(i1, o2.Inner)
False

(สิ่งนี้ใช้ผู้ตกแต่งคลาสซึ่งเป็นของใหม่ใน Python 2.6 และ 3.0 มิฉะนั้นคุณจะต้องพูดว่า“ Inner = innerclass (Inner)” หลังนิยามคลาส)


5
use-cases ที่เรียกสิ่งนั้น (เช่น Java-esque inner คลาสซึ่งอินสแตนซ์มีความสัมพันธ์กับอินสแตนซ์ของคลาสภายนอก) โดยทั่วไปสามารถแก้ไขได้ใน Python โดยกำหนดคลาสภายในภายในเมธอดของคลาสนอก - พวกเขาจะเห็น ด้านนอกselfโดยไม่ต้องใช้งานเพิ่มเติมใด ๆ (เพียงใช้ตัวระบุอื่นที่คุณมักจะใส่ด้านในselfเช่นinnerself) และจะสามารถเข้าถึงอินสแตนซ์ภายนอกผ่านทางนั้นได้
Evgeni Sergeev

การใช้WeakKeyDictionaryในตัวอย่างนี้ไม่อนุญาตให้รวบรวมคีย์โดยแท้เนื่องจากค่าดังกล่าวอ้างอิงคีย์ตามลำดับอย่างมากผ่านownerแอตทริบิวต์
Kritzefitz

36

มีบางอย่างที่คุณต้องห่อหัวเพื่อให้เข้าใจเรื่องนี้ ในภาษาส่วนใหญ่นิยามคลาสเป็นคำสั่งไปยังคอมไพเลอร์ นั่นคือคลาสจะถูกสร้างขึ้นก่อนที่โปรแกรมจะทำงาน ใน python คำสั่งทั้งหมดสามารถเรียกใช้งานได้ นั่นหมายความว่าคำสั่งนี้:

class foo(object):
    pass

เป็นคำสั่งที่ดำเนินการที่รันไทม์เช่นเดียวกับคำสั่งนี้:

x = y + z

ซึ่งหมายความว่าไม่เพียง แต่คุณสามารถสร้างชั้นเรียนภายในชั้นเรียนอื่น ๆ ได้ แต่คุณยังสามารถสร้างชั้นเรียนได้ทุกที่ที่คุณต้องการ พิจารณารหัสนี้:

def foo():
    class bar(object):
        ...
    z = bar()

ดังนั้นแนวคิดของ "ชั้นใน" จึงไม่ใช่โครงสร้างภาษา มันเป็นโปรแกรมเมอร์ที่สร้าง กุยโดมีบทสรุปที่ดีมากว่ามันมาที่นี่ได้อย่างไร แต่โดยพื้นฐานแล้วแนวคิดพื้นฐานคือสิ่งนี้ทำให้ไวยากรณ์ของภาษาง่ายขึ้น


16

การซ้อนชั้นเรียนภายในชั้นเรียน:

  • คลาสที่ซ้อนกันขยายคำจำกัดความของคลาสทำให้ยากที่จะเห็นว่าเกิดอะไรขึ้น

  • คลาสที่ซ้อนกันสามารถสร้างการเชื่อมต่อที่จะทำให้การทดสอบยากขึ้น

  • ใน Python คุณสามารถใส่มากกว่าหนึ่งคลาสในไฟล์ / โมดูลซึ่งแตกต่างจาก Java ดังนั้นคลาสยังคงอยู่ใกล้กับคลาสระดับบนสุดและอาจมีชื่อคลาสที่นำหน้าด้วย "_" เพื่อช่วยบ่งบอกว่าคนอื่นไม่ควรเป็น ใช้มัน

สถานที่ที่คลาสที่ซ้อนกันสามารถพิสูจน์ได้ว่ามีประโยชน์อยู่ในฟังก์ชัน

def some_func(a, b, c):
   class SomeClass(a):
      def some_method(self):
         return b
   SomeClass.__doc__ = c
   return SomeClass

คลาสรวบรวมค่าจากฟังก์ชันช่วยให้คุณสร้างคลาสแบบไดนามิกเช่นการเขียนโปรแกรมแม่แบบใน C ++


7

ฉันเข้าใจข้อโต้แย้งของคลาสที่ซ้อนกัน แต่มีบางกรณีที่จะใช้ในบางครั้ง ลองนึกภาพว่าฉันกำลังสร้างคลาสรายการที่เชื่อมโยงแบบทวีคูณและฉันต้องสร้างคลาสโหนดสำหรับการบำรุงรักษาโหนด ฉันมีสองทางเลือกสร้างคลาส Node ภายในคลาส DoublyLinkedList หรือสร้างคลาส Node นอกคลาส DoublyLinkedList ฉันชอบตัวเลือกแรกในกรณีนี้เนื่องจากคลาส Node มีความหมายเฉพาะในคลาส DoublyLinkedList แม้ว่าจะไม่มีประโยชน์ในการซ่อน / ห่อหุ้ม แต่ก็มีประโยชน์ในการจัดกลุ่มที่สามารถบอกว่าคลาส Node เป็นส่วนหนึ่งของคลาส DoublyLinkedList


5
นี่เป็นความจริงโดยสมมติว่าNodeคลาสเดียวกันไม่มีประโยชน์สำหรับคลาสลิสต์ที่เชื่อมโยงประเภทอื่น ๆ ที่คุณอาจสร้างขึ้นด้วยซึ่งในกรณีนี้มันน่าจะอยู่ภายนอก
Acumenus

อีกวิธีหนึ่งในการวางมัน: Nodeอยู่ภายใต้เนมสเปซDoublyLinkedListและมันก็สมเหตุสมผลที่จะเป็นเช่นนั้น นี่คือ Pythonic: "เนมสเปซเป็นแนวคิดที่ยอดเยี่ยมอย่างหนึ่ง - มาทำสิ่งเหล่านั้นให้มากขึ้น!"
cbarrick

@cbarrick: การทำ "มากกว่านั้น" ไม่มีอะไรเกี่ยวกับการทำรัง
Ethan Furman

4

มีบางสิ่งที่ไม่สามารถทำได้หากไม่มีพวกเขา? ถ้าเป็นเช่นนั้นสิ่งนั้นคืออะไร?

มีอะไรบางอย่างที่ไม่สามารถทำได้อย่างง่ายดายโดยไม่ต้อง : มรดกของการเรียนที่เกี่ยวข้อง

นี่คือตัวอย่างมินิมอลที่มีคลาสที่เกี่ยวข้องAและB:

class A(object):
    class B(object):
        def __init__(self, parent):
            self.parent = parent

    def make_B(self):
        return self.B(self)


class AA(A):  # Inheritance
    class B(A.B):  # Inheritance, same class name
        pass

รหัสนี้นำไปสู่พฤติกรรมที่สมเหตุสมผลและคาดเดาได้:

>>> type(A().make_B())
<class '__main__.A.B'>
>>> type(A().make_B().parent)
<class '__main__.A'>
>>> type(AA().make_B())
<class '__main__.AA.B'>
>>> type(AA().make_B().parent)
<class '__main__.AA'>

หากBเป็นคลาสระดับบนสุดคุณไม่สามารถเขียนself.B()ในเมธอดได้make_Bแต่จะเขียนเพียงอย่างB()เดียวจึงสูญเสียการเชื่อมโยงแบบไดนามิกกับคลาสที่เพียงพอ

ทราบว่าในการก่อสร้างนี้คุณไม่ควรอ้างถึงชั้นในร่างกายของชั้นเรียนA Bนี้เป็นแรงจูงใจสำหรับการแนะนำแอตทริบิวต์ในชั้นเรียนparentB

แน่นอนว่าการเชื่อมโยงแบบไดนามิกนี้สามารถสร้างขึ้นใหม่ได้โดยไม่ต้องใช้คลาสภายในด้วยค่าใช้จ่ายของเครื่องมือที่น่าเบื่อและเกิดข้อผิดพลาดของคลาส


1

กรณีการใช้งานหลักที่ฉันใช้คือการป้องกันการแพร่กระจายของโมดูลขนาดเล็กและเพื่อป้องกันมลพิษของเนมสเปซเมื่อไม่จำเป็นต้องใช้โมดูลแยกต่างหาก หากฉันกำลังขยายคลาสที่มีอยู่ แต่คลาสที่มีอยู่นั้นจะต้องอ้างอิงคลาสย่อยอื่นที่ควรคู่กับคลาสนั้นเสมอ ตัวอย่างเช่นฉันอาจมีutils.pyโมดูลที่มีคลาสตัวช่วยมากมายซึ่งไม่จำเป็นต้องอยู่คู่กัน แต่ฉันต้องการเสริมสร้างการมีเพศสัมพันธ์สำหรับคลาสตัวช่วยบางคลาส ตัวอย่างเช่นเมื่อฉันใช้https://stackoverflow.com/a/8274307/2718295

: utils.py:

import json, decimal

class Helper1(object):
    pass

class Helper2(object):
    pass

# Here is the notorious JSONEncoder extension to serialize Decimals to JSON floats
class DecimalJSONEncoder(json.JSONEncoder):

    class _repr_decimal(float): # Because float.__repr__ cannot be monkey patched
        def __init__(self, obj):
            self._obj = obj
        def __repr__(self):
            return '{:f}'.format(self._obj)

    def default(self, obj): # override JSONEncoder.default
        if isinstance(obj, decimal.Decimal):
            return self._repr_decimal(obj)
        # else
        super(self.__class__, self).default(obj)
        # could also have inherited from object and used return json.JSONEncoder.default(self, obj) 

จากนั้นเราสามารถ:

>>> from utils import DecimalJSONEncoder
>>> import json, decimal
>>> json.dumps({'key1': decimal.Decimal('1.12345678901234'), 
... 'key2':'strKey2Value'}, cls=DecimalJSONEncoder)
{"key2": "key2_value", "key_1": 1.12345678901234}

แน่นอนเราสามารถละทิ้งการสืบทอดjson.JSONEnocderทั้งหมดและลบล้างค่าเริ่มต้น ():

:

import decimal, json

class Helper1(object):
    pass

def json_encoder_decimal(obj):
    class _repr_decimal(float):
        ...

    if isinstance(obj, decimal.Decimal):
        return _repr_decimal(obj)

    return json.JSONEncoder(obj)


>>> json.dumps({'key1': decimal.Decimal('1.12345678901234')}, default=json_decimal_encoder)
'{"key1": 1.12345678901234}'

แต่บางครั้งเพียงเพื่อการประชุมคุณต้องการutilsประกอบด้วยชั้นเรียนสำหรับการขยาย

นี่คือกรณีการใช้งานอื่น: ฉันต้องการโรงงานสำหรับ mutables ใน OuterClass ของฉันโดยไม่ต้องเรียกใช้copy:

class OuterClass(object):

    class DTemplate(dict):
        def __init__(self):
            self.update({'key1': [1,2,3],
                'key2': {'subkey': [4,5,6]})


    def __init__(self):
        self.outerclass_dict = {
            'outerkey1': self.DTemplate(),
            'outerkey2': self.DTemplate()}



obj = OuterClass()
obj.outerclass_dict['outerkey1']['key2']['subkey'].append(4)
assert obj.outerclass_dict['outerkey2']['key2']['subkey'] == [4,5,6]

ฉันชอบรูปแบบนี้มากกว่า@staticmethodมัณฑนากรที่คุณจะใช้สำหรับฟังก์ชั่นโรงงาน


1

1. สองวิธีเทียบเท่าการทำงาน

สองวิธีที่แสดงก่อนหน้านั้นเหมือนกันในการทำงาน อย่างไรก็ตามมีความแตกต่างเล็กน้อยและมีบางสถานการณ์ที่คุณต้องการเลือกอย่างใดอย่างหนึ่ง

วิธีที่ 1: นิยามคลาสที่ซ้อนกัน
(= "คลาสที่ซ้อนกัน")

class MyOuter1:
    class Inner:
        def show(self, msg):
            print(msg)

วิธีที่ 2: ด้วยระดับโมดูลชั้นในที่แนบกับคลาสชั้นนอก
(= "คลาสภายในที่อ้างอิง")

class _InnerClass:
    def show(self, msg):
        print(msg)

class MyOuter2:
    Inner = _InnerClass

ขีดล่างใช้เพื่อติดตามอินเทอร์เฟซภายในของPEP8 "(แพ็กเกจโมดูลคลาสฟังก์ชันคุณลักษณะหรือชื่ออื่น ๆ ) ควรขึ้นต้นด้วยขีดล่างนำหน้าเดียว"

2. ความคล้ายคลึงกัน

ด้านล่างข้อมูลโค้ดแสดงให้เห็นถึงความคล้ายคลึงกันของฟังก์ชันของ "คลาสที่ซ้อนกัน" กับ "คลาสภายในที่อ้างอิง" พวกเขาจะทำงานในลักษณะเดียวกันในการตรวจสอบโค้ดสำหรับประเภทของอินสแตนซ์คลาสภายใน ไม่จำเป็นต้องพูดว่าพวกm.inner.anymethod()เขาจะทำงานในทำนองเดียวกันกับm1และm2

m1 = MyOuter1()
m2 = MyOuter2()

innercls1 = getattr(m1, 'Inner', None)
innercls2 = getattr(m2, 'Inner', None)

isinstance(innercls1(), MyOuter1.Inner)
# True

isinstance(innercls2(), MyOuter2.Inner)
# True

type(innercls1()) == mypackage.outer1.MyOuter1.Inner
# True (when part of mypackage)

type(innercls2()) == mypackage.outer2.MyOuter2.Inner
# True (when part of mypackage)

3. ความแตกต่าง

ความแตกต่างของ "คลาสที่ซ้อนกัน" และ "คลาสภายในที่อ้างอิง" แสดงอยู่ด้านล่าง พวกเขาไม่ใหญ่ แต่บางครั้งคุณอาจต้องการเลือกอย่างใดอย่างหนึ่งตามสิ่งเหล่านี้

3.1 การห่อหุ้มรหัส

ด้วย "คลาสที่ซ้อนกัน" จะสามารถห่อหุ้มโค้ดได้ดีกว่าการใช้ "คลาสภายในที่อ้างอิง" คลาสในเนมสเปซของโมดูลเป็นตัวแปรส่วนกลาง วัตถุประสงค์ของคลาสที่ซ้อนกันคือเพื่อลดความยุ่งเหยิงในโมดูลและทำให้คลาสชั้นในอยู่ในคลาสชั้นนอก

ในขณะที่ไม่มีใครใช้ * from packagename import *ตัวแปรระดับโมดูลในปริมาณที่ต่ำอาจเป็นสิ่งที่ดีตัวอย่างเช่นเมื่อใช้ IDE ที่มีการเติมโค้ด / intellisense

* ใช่ไหม?

3.2 การอ่านรหัส

เอกสาร Django แนะนำให้ใช้Meta คลาสภายในสำหรับข้อมูลเมตาของโมเดล มันชัดเจนกว่าเล็กน้อย * ในการสั่งให้ผู้ใช้เฟรมเวิร์กเขียนclass Foo(models.Model)ด้วย inner class Meta;

class Ox(models.Model):
    horn_length = models.IntegerField()

    class Meta:
        ordering = ["horn_length"]
        verbose_name_plural = "oxen"

แทนที่จะ "เขียน a class _Metaแล้วเขียนclass Foo(models.Model)ด้วยMeta = _Meta";

class _Meta:
    ordering = ["horn_length"]
    verbose_name_plural = "oxen"

class Ox(models.Model):
    Meta = _Meta
    horn_length = models.IntegerField()
  • ด้วยวิธี "คลาสที่ซ้อนกัน" โค้ดสามารถอ่านรายการสัญลักษณ์แสดงหัวข้อย่อยที่ซ้อนกันได้แต่ด้วยเมธอด "คลาสภายในที่อ้างอิง" จะต้องเลื่อนกลับขึ้นไปเพื่อดูคำจำกัดความของ_Metaการดู "ไอเท็มลูก" (แอตทริบิวต์)

  • เมธอด "คลาสภายในที่อ้างอิง" สามารถอ่านได้มากขึ้นหากระดับการซ้อนโค้ดของคุณเพิ่มขึ้นหรือแถวยาวด้วยเหตุผลอื่น

* แน่นอนว่าเป็นเรื่องของรสชาติ

3.3 ข้อความแสดงข้อผิดพลาดที่แตกต่างกันเล็กน้อย

นี่ไม่ใช่เรื่องใหญ่ แต่เพื่อความสมบูรณ์: เมื่อเข้าถึงแอตทริบิวต์ที่ไม่มีอยู่จริงสำหรับคลาสชั้นในเราจะเห็นข้อยกเว้นที่แตกต่างกันเล็กน้อย ดำเนินการตามตัวอย่างที่ให้ไว้ในส่วนที่ 2:

innercls1.foo()
# AttributeError: type object 'Inner' has no attribute 'foo'

innercls2.foo()
# AttributeError: type object '_InnerClass' has no attribute 'foo'

นี่เป็นเพราะtypeคลาสภายในคือ

type(innercls1())
#mypackage.outer1.MyOuter1.Inner

type(innercls2())
#mypackage.outer2._InnerClass

0

ฉันได้ใช้คลาสภายในของ Python เพื่อสร้างคลาสย่อยที่เป็นบั๊กโดยเจตนาภายในฟังก์ชันที่ไม่ได้ใช้งานมากที่สุด (เช่นภายในdef test_something():) เพื่อให้สามารถครอบคลุมการทดสอบได้มากขึ้น 100% (เช่นการทดสอบคำสั่งการบันทึกที่แทบไม่ได้เรียกใช้โดยการแทนที่วิธีการบางอย่าง)

หากมองย้อนกลับไปจะคล้ายกับคำตอบของ Ed https://stackoverflow.com/a/722036/1101109

ชั้นเรียนภายในดังกล่าวควรอยู่นอกขอบเขตและพร้อมสำหรับการรวบรวมขยะเมื่อการอ้างอิงทั้งหมดถูกลบออกไป ตัวอย่างเช่นใช้inner.pyไฟล์ต่อไปนี้:

class A(object):
    pass

def scope():
    class Buggy(A):
        """Do tests or something"""
    assert isinstance(Buggy(), A)

ฉันได้รับผลลัพธ์ที่น่าสงสัยต่อไปนี้ภายใต้ OSX Python 2.7.6:

>>> from inner import A, scope
>>> A.__subclasses__()
[]
>>> scope()
>>> A.__subclasses__()
[<class 'inner.Buggy'>]
>>> del A, scope
>>> from inner import A
>>> A.__subclasses__()
[<class 'inner.Buggy'>]
>>> del A
>>> import gc
>>> gc.collect()
0
>>> gc.collect()  # Yes I needed to call the gc twice, seems reproducible
3
>>> from inner import A
>>> A.__subclasses__()
[]

คำแนะนำ - อย่าดำเนินการต่อและลองทำสิ่งนี้กับโมเดล Django ซึ่งดูเหมือนจะเก็บการอ้างอิงอื่น ๆ (แคช?) ไว้ในคลาสรถบั๊กกี้ของฉัน

โดยทั่วไปแล้วฉันจะไม่แนะนำให้ใช้คลาสภายในเพื่อวัตถุประสงค์ประเภทนี้เว้นแต่คุณจะให้ความสำคัญกับการทดสอบ 100% และไม่สามารถใช้วิธีอื่นได้ แม้ว่าฉันคิดว่ามันเป็นเรื่องดีที่ได้ทราบว่าหากคุณใช้__subclasses__()มันบางครั้งอาจทำให้คนชั้นในปนเปื้อนได้ ไม่ว่าจะด้วยวิธีใดถ้าคุณติดตามมาจนถึงตอนนี้ฉันคิดว่าเราค่อนข้างลึกเข้าไปใน Python ณ จุดนี้ดันเดอร์สคอร์ส่วนตัวและทั้งหมด


3
ทั้งหมดนี้ไม่เกี่ยวกับคลาสย่อยไม่ใช่คลาสภายในเหรอ ?? ก
klaas

ในกล่องด้านบน Buggy สืบทอดมาจาก A. ดังนั้นsublassแสดงให้เห็นว่า ยังเห็นในตัวฟังก์ชั่นissubclass ()
Klaas

ขอบคุณ @klaas ฉันคิดว่ามันน่าจะชัดเจนขึ้นว่าฉันแค่ใช้.__subclasses__()เพื่อทำความเข้าใจว่าคลาสภายในโต้ตอบกับตัวเก็บขยะอย่างไรเมื่อสิ่งต่างๆอยู่นอกขอบเขตใน Python สิ่งนี้ดูเหมือนจะครอบงำโพสต์ด้วยสายตาดังนั้น 1-3 ย่อหน้าแรกจึงสมควรได้รับการขยายเพิ่มเติมเล็กน้อย
pzrq
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.