source

파이썬 내부 수업의 목적은 무엇입니까?

factcode 2023. 9. 11. 22:15
반응형

파이썬 내부 수업의 목적은 무엇입니까?

파이썬의 속/둥근 수업은 저를 혼란스럽게 합니다.그들 없이는 이룰 수 없는 일이 있습니까?만약 그렇다면, 저것은 무엇입니까?

http://www.geekinterview.com/question_details/64739 에서 인용:

내부 클래스의 장점:

  • 클래스의 논리적 그룹화:클래스가 한 개의 다른 클래스에만 유용한 경우 해당 클래스에 포함시켜 두 개를 함께 유지하는 것이 논리적입니다.이러한 "도우미 클래스"를 네스팅하면 패키지가 더 간소화됩니다.
  • 캡슐화 증가:그렇지 않으면 비공개로 선언될 수 있는 A의 멤버에 대한 접근이 필요한 두 개의 최상위 클래스 A와 B를 생각해 보십시오.클래스 A 내에 클래스 B를 숨겨 A의 구성원을 비공개로 선언하고 B가 접근할 수 있습니다.게다가 B 자체는 외부 세계로부터 숨겨질 수 있습니다.
  • 보다 판독성이 높고 유지보수가 가능한 코드:최상위 클래스 내에 작은 클래스를 중첩하면 코드가 사용되는 위치에 더 가깝게 배치됩니다.

주된 장점은 조직입니다.내면의 계급으로 성취할 수 있는 것은 무엇이든 내면의 계급 없이 성취할 수 있습니다.

그들 없이는 이룰 수 없는 일이 있습니까?

아니요. 클래스를 최상위 레벨에서 일반적으로 정의한 다음 참조를 외부 클래스에 복사하는 것과 완전히 같습니다.

내포된 클래스가 '허용'되는 특별한 이유는 없다고 생각합니다. 다만, 명시적으로 '허용'하지 않는 것은 특별한 의미가 없습니다.

외부/소유자 개체의 라이프사이클 내에 존재하며 항상 외부 클래스의 인스턴스(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= inner class(Inner)"라고 말해야 합니다.)

이를 이해하기 위해서는 머리를 감싸야 할 것이 있습니다.대부분의 언어에서 클래스 정의는 컴파일러에 대한 지시사항입니다.즉, 프로그램이 실행되기 전에 클래스가 생성됩니다.python에서는 모든 문이 실행 가능합니다.이 말은 다음과 같은 뜻입니다.

class foo(object):
    pass

는 다음과 같이 런타임 시에 실행되는 문입니다.

x = y + z

이는 다른 클래스 내에서 클래스를 만들 수 있을 뿐만 아니라 원하는 곳에 클래스를 만들 수 있음을 의미합니다.다음 코드를 고려합니다.

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

따라서 "내부 수업"의 개념은 언어 구성요소가 아니라 프로그래머 구성요소입니다.Guido는 이것이 어떻게 여기에 왔는지에 대한 아주 좋은 요약을 가지고 있습니다.하지만 기본적인 생각은 이것이 언어의 문법을 단순화시킨다는 것입니다.

클래스 내에 클래스를 중첩:

  • 중첩된 클래스는 클래스 정의를 부풀려서 무슨 일이 일어나고 있는지 보기가 더 어렵습니다.

  • 중첩된 클래스는 결합을 생성하여 테스트를 더 어렵게 만들 수 있습니다.

  • Python에서는 Java와 달리 파일/모듈에 둘 이상의 클래스를 넣을 수 있으므로 클래스는 여전히 최상위 클래스에 가깝게 유지되며 클래스 이름 앞에 "_"을 붙여 다른 사용자가 사용하지 않아야 함을 나타낼 수 있습니다.

중첩 클래스가 유용함을 증명할 수 있는 위치는 함수 내에 있습니다.

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

클래스는 함수에서 값을 캡처하여 C++에서 템플릿 메타프로그래밍과 같은 클래스를 동적으로 만들 수 있습니다.

중첩 수업에 반대하는 주장은 이해하지만, 경우에 따라 사용하는 경우가 있습니다.제가 이중으로 연결된 목록 클래스를 만들고 노드를 유지하기 위한 노드 클래스를 만들어야 한다고 생각해보세요.DoubleLinkedList 클래스 안에 Node 클래스를 만들거나 DoubleLinkedList 클래스 밖에 Node 클래스를 만들 수 있습니다.이 경우 Node 클래스는 DoubleLinkedList 클래스 내에서만 의미가 있기 때문에 첫 번째 선택을 선호합니다.숨김/캡슐화 이점은 없지만 노드 클래스가 DoubleLinkedList 클래스의 일부라고 말할 수 있는 그룹화 이점이 있습니다.

그들 없이는 이룰 수 없는 일이 있습니까?만약 그렇다면, 저것은 무엇입니까?

없이는 쉽게 할없는 것이 있는데 바로 관련 계층의 상속입니다.

은 관련 의 가 의 가 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였습니다.위로쓸수다다수쓸tee위로d,au-slself.B()nmake_B하지만 단순히 글을 쓰는 것 뿐입니다.B(), 따라서 적절한 클래스에 대한 동적 바인딩을 잃게 됩니다.

이 공사에서는 클래스를 참조하면 안 됩니다.A반에서B. 이것이 도입 동기입니다.parent수업속의 속성B.

Of course, this dynamic binding can be recreated without inner class at the cost of a tedious and error-prone instrumentation of the classes.

1. Two functionally equivalent ways

The two ways shown before are functionally identical. However, there are some subtle differences, and there are situations when you would like to choose one over another.

방법 1: 중첩 클래스 정의
(="내포 클래스")

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

방법 2: 모듈 레벨 내부 클래스가 외부 클래스에 부착된 경우
(="기준 내부 클래스")

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

class MyOuter2:
    Inner = _InnerClass

Underscore is used to follow PEP8 "internal interfaces (packages, modules, classes, functions, attributes or other names) should -- be prefixed with a single leading underscore."

2. Similarities

아래 코드 조각은 "네스트 클래스"와 "레퍼런스된 내부 클래스"의 기능적 유사성을 보여줍니다.내부 클래스 인스턴스 유형에 대한 코드 검사에서도 동일한 방식으로 동작합니다.말할 것도 없이,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. Differences

The differences of "Nested class" and "Referenced inner class" are listed below. They are not big, but sometimes you would like to choose one or the other based on these.

3.1 Code Encapsulation

With "Nested classes" it is possible to encapsulate code better than with "Referenced inner class". A class in the module namespace is a global variable. The purpose of nested classes is to reduce clutter in the module and put the inner class inside the outer class.

사용자 없음*이(가)from packagename import *, 예를 들어 코드 완성/인텔리센스가 있는 IDE를 사용할 때 모듈 수준 변수의 양이 적으면 좋을 수 있습니다.

*Right?

3.2 Readability of code

장고 문서에서는 모델 메타데이터에 내부 클래스 메타를 사용하도록 지시합니다.프레임워크 사용자에게 a를 작성하도록 지시하는 것이 좀 더 명확합니다*class Foo(models.Model)내심으로class Meta;

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

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

"write a" 대신class _Meta, 그리고 a를 씁니다.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()
  • "Nested class" 접근 방식에서는 코드를 중첩된 글머리 기호 목록으로 읽을있지만, "Referenced inner class" 방법에서는 스크롤을 뒤로 돌려야 다음 정의를 볼 수 있습니다._Meta"자녀 항목"(속성물)을 볼 수 있습니다.

  • 코드 네스팅 수준이 증가하거나 다른 이유로 행이 긴 경우 "Referenced inner class" 메서드를 더 잘 읽을 수 있습니다.

* 물론, 취향의 문제죠.

3.3 약간 다른 오류 메시지

이것은 큰 문제가 아니지만, 단지 완성도를 위해서입니다.내부 클래스에 대해 존재하지 않는 속성에 액세스할 때 약간 다른 예외가 나타납니다.섹션 2에 제시된 예를 계속합니다.

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

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

은 이기 입니다.type는 s입니다.

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

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

제가 주로 사용하는 사용 사례는 작은 모듈의 확산을 방지하고 별도의 모듈이 필요 없을 때 네임스페이스 오염을 방지하는 것입니다.기존 클래스를 확장하지만 해당 클래스가 항상 연결되어야 하는 다른 하위 클래스를 참조해야 하는 경우.예를 들어, 나는 아마도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모두 함께 default(기본값)을 덮어씁니다.

:

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확장성을 위한 클래스로 구성됩니다.

다음은 또 다른 사용 사례입니다.호출할 필요 없이 Outer Class에서 가변 가능한 공장을 원합니다.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그렇지 않으면 공장 기능에 사용할 데코레이터.

저는 파이썬의 내부 클래스를 사용하여 유니테스트 함수(즉, 내부) 내에 의도적으로 버그가 있는 하위 클래스를 만들었습니다.def test_something():100% 테스트 적용 범위에 근접하기 위해(예: 일부 방법을 재정의하여 매우 드물게 트리거된 로깅 문을 테스트).

돌이켜보면 에드의 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__()
[]

힌트 - 계속해서 장고 모델을 사용해 보지 마십시오. 장고 모델은 버그 클래스에 대한 다른(캐시된?) 참조를 유지하는 것 같습니다.

따라서 일반적으로 100% 테스트 적용 범위에 가치를 두고 다른 방법을 사용할 수 없는 경우가 아니라면 이러한 목적으로 내부 클래스를 사용하는 것을 권장하지 않습니다.제가 생각하기엔 좋은 것 같아요. 만약에 당신이 그 컴퓨터를 사용한다면.__subclasses__(), 그것은 때때로 내부 계급에 의해 오염될 수 있습니다.어느 쪽이든 여기까지 따라오셨다면, 저는 우리가 파이썬에 깊이 빠져있다고 생각합니다. 개인적인 점수 등등.

언급URL : https://stackoverflow.com/questions/719705/what-is-the-purpose-of-pythons-inner-classes

반응형