모듈은 객체와 같은 방식으로 속성을 가질 수 있습니까?


100

파이썬 속성을 사용하면 다음과 같이 만들 수 있습니다.

obj.y 

값을 반환하는 대신 함수를 호출합니다.

모듈로이를 수행하는 방법이 있습니까? 내가 원하는 케이스가 있어요

module.y 

거기에 저장된 값을 반환하는 대신 함수를 호출합니다.


2
보다 현대적인 솔루션 __getattr__은 모듈 을 참조하십시오 .
WIM

답변:


56

새 스타일 클래스의 인스턴스 만 속성을 가질 수 있습니다. 그런 인스턴스를 sys.modules[thename] = theinstance. 예를 들어 m.py 모듈 파일은 다음과 같을 수 있습니다.

import sys

class _M(object):
    def __init__(self):
        self.c = 0
    def afunction(self):
        self.c += 1
        return self.c
    y = property(afunction)

sys.modules[__name__] = _M()

2
다른 사람이 이것을 시도 했습니까? 이 코드를 x.py 파일에 넣고 다른 파일에서 가져 오면 xy를 호출하면 AttributeError : 'NoneType'object has no attribute 'c', because _M has any value have None ...
Stephan202

3
실제로 코드는 인터프리터에서 작동합니다. 하지만 파일 (예 : bowwow.py)에 넣고 다른 파일 (otherfile.py)에서 가져 오면 더 이상 작동하지 않습니다.
Stephan202

4
Q : @Unknown types.ModuleType의 다른 매우 유사한 답변에서 볼 수 있듯이 인스턴스의 클래스를 파생시키는 데 특별한 이점이 있습니까?
martineau

11
새 스타일 클래스의 인스턴스 만 속성을 가질 수 있습니다. 이것이 이유가 아닙니다. 모듈 새로운 스타일 클래스의 인스턴스입니다 builtins.module. 즉, 자체가 인스턴스 type(새 스타일 클래스의 정의) 인의 인스턴스입니다 . 문제는 속성이 클래스 인스턴스가 아니라에 있어야한다는 것입니다 : 당신이 할 경우 f = Foo(), f.some_property = property(...)당신은 순진하게도 모듈에 넣어 경우와 같은 방법으로 실패 할 수 있습니다. 해결책은 그것을 클래스에 넣는 것이지만 모든 모듈이 속성을 갖기를 원하지 않기 때문에 하위 클래스를 만듭니다 (Unknown의 답변 참조).
Thanatos

3
@Joe, 이름 globals()을 다시 None바인딩 할 때 ( 키는 그대로 유지하지만 값을로 재설정하는 ) 변경 sys.modules은 Python 2 문제입니다 .Python 3.4는 의도 한대로 작동합니다. Py2의 클래스 객체에 액세스해야하는 경우 예를 들어 명령문 _M._cls = _M바로 뒤에 추가 하고 class(또는 다른 네임 스페이스에 동등하게 숨겨 둡니다)이를 self._cls요구하는 메서드에서 와 같이 액세스 합니다 (의 type(self)서브 클래 싱도 수행하는 경우 괜찮을 수 있지만 _M). .
Alex Martelli

54

모듈의 모든 속성을 올바르게 상속하고 isinstance ()로 올바르게 식별하기 위해 이렇게합니다.

import types

class MyModule(types.ModuleType):
    @property
    def y(self):
        return 5


>>> a=MyModule("test")
>>> a
<module 'test' (built-in)>
>>> a.y
5

그런 다음 이것을 sys.modules에 삽입 할 수 있습니다.

sys.modules[__name__] = MyModule(__name__)  # remember to instantiate the class

이것은 가장 단순한 경우에만 작동하는 것 같습니다. 가능한 문제는 다음과 같습니다. (1) 일부 가져 오기 도우미는 __file__수동으로 정의해야하는 다른 속성도 예상 할 수 있습니다. (2) 클래스를 포함하는 모듈에서 만든 가져 오기는 런타임 중에 "보이지"않는 등 ...
tutuDajuju

1
그것은에서 서브 클래스 도출 할 필요가 없습니다 types.ModuleType, 어떤 (새로운 스타일의) 클래스가 할 것입니다. 상속하려는 특수 모듈 속성은 정확히 무엇입니까?
martineau 2016

원래 모듈이 패키지이고 원래 모듈 아래에있는 모듈에 액세스하려면 어떻게해야합니까?
kawing-chiu

2
@martineau 모듈 repr이 있고 __init__, 인스턴스 일 때 모듈 이름을 지정할 수 있으며 , isinstance.
WIM

@wim : 솔직히 그다지 중요한 IMO가 아닌 것 같습니다.
martineau

35

으로 PEP (562)는 파이썬으로 구현되었습니다> = 3.7, 이제 우리는이 작업을 수행 할 수 있습니다

파일 : module.py

def __getattr__(name):
    if name == 'y':
        return 3
    raise AttributeError(f"module '{__name__}' has no attribute '{name}'")

other = 4

용법:

>>> import module
>>> module.y
3
>>> module.other
4
>>> module.nosuch
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "module.py", line 4, in __getattr__
    raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
AttributeError: module 'module' has no attribute 'nosuch'

당신이를 생략하면 참고 raise AttributeError__getattr__기능, 그것은을 가진 함수 끝을 의미 return None, 그 다음이 module.nosuch값을 얻을 것이다 None.


2
이를 바탕으로 다른 답변을 추가했습니다. stackoverflow.com/a/58526852/2124834

3
이것은 재산의 절반에 불과합니다. 세터가 없습니다.

안타깝게도 툴링이 이러한 속성 (?)을 인식하도록 만드는 것은 어렵지 않은 것 같습니다 ( getattr 은 정규 멤버가없는 경우에만 호출 됨)
olejorgenb

9

을 바탕으로 존 린의 대답 :

def module_property(func):
    """Decorator to turn module functions into properties.
    Function names must be prefixed with an underscore."""
    module = sys.modules[func.__module__]

    def base_getattr(name):
        raise AttributeError(
            f"module '{module.__name__}' has no attribute '{name}'")

    old_getattr = getattr(module, '__getattr__', base_getattr)

    def new_getattr(name):
        if f'_{name}' == func.__name__:
            return func()
        else:
            return old_getattr(name)

    module.__getattr__ = new_getattr
    return func

사용 (선도 밑줄 주)에서 the_module.py:

@module_property
def _thing():
    return 'hello'

그때:

import the_module

print(the_module.thing)  # prints 'hello'

속성 화 된 함수를 원래 함수와 구별하려면 선행 밑줄이 필요합니다. 데코레이터가 실행되는 동안 식별자가 아직 할당되지 않았기 때문에 식별자를 다시 할당하는 방법을 생각할 수 없었습니다.

IDE는 속성이 존재하는지 알지 못하며 빨간색 wavie를 표시합니다.


큰! 클래스 속성과 비교할 때 밑줄이없는 것이 더 일반적 @property def x(self): return self._x이라고 생각 def thing()합니다. 그리고 대답에 "모듈 속성 설정자"데코레이터를 만들 수 있습니까?
John Lin 19

2
@JohnLin, 귀하의 def thing()제안 을 구현하려고 시도했습니다 . 문제는 누락 된 속성에__getattr__ 대해서만 호출 된다는 입니다. 그러나 @module_property def thing(): …실행 후에 the_module.thing는 정의되므로 getattr 이 호출되지 않습니다. thing데코레이터 에 어떻게 든 등록한 다음 모듈의 네임 스페이스에서 삭제해야합니다. 나는 돌아 시도 None데코레이터에서,하지만 다음 thing과 같이 정의된다 None. 하나는 할 수 @module_property def thing(): … del thing있지만 thing()함수로 사용 하는 것보다 더 나쁘다는 것을 알았습니다
Ben Mares

__getattribute__좋습니다. "모듈 속성 설정자"나 "모듈 "이 없습니다. 감사합니다.
John Lin

5

일반적인 사용 사례는 다음과 같습니다. 모든 모듈 항목을 클래스 레이아웃으로 전환하지 않고 일부 (몇 가지) 동적 속성을 사용하여 기존 모듈을 풍부하게합니다. 불행히도 가장 간단한 모듈 클래스 패치 sys.modules[__name__].__class__ = MyPropertyModuleTypeError: __class__ assignment: only for heap types. 따라서 모듈 생성을 다시 연결해야합니다.

이 접근 방식은 모듈 코드 위에 프롤로그를 추가하여 Python 가져 오기 후크없이 수행합니다.

# propertymodule.py
""" Module property example """

if '__orgmod__' not in globals():

    # constant prolog for having module properties / supports reload()

    print "PropertyModule stub execution", __name__
    import sys, types
    class PropertyModule(types.ModuleType):
        def __str__(self):
            return "<PropertyModule %r from %r>" % (self.__name__, self.__file__)
    modnew = PropertyModule(__name__, __doc__)
    modnew.__modclass__ = PropertyModule        
    modnew.__file__ = __file__
    modnew.__orgmod__ = sys.modules[__name__]
    sys.modules[__name__] = modnew
    exec sys._getframe().f_code in modnew.__dict__

else:

    # normal module code (usually vast) ..

    print "regular module execution"
    a = 7

    def get_dynval(module):
        return "property function returns %s in module %r" % (a * 4, module.__name__)    
    __modclass__.dynval = property(get_dynval)

용법:

>>> import propertymodule
PropertyModule stub execution propertymodule
regular module execution
>>> propertymodule.dynval
"property function returns 28 in module 'propertymodule'"
>>> reload(propertymodule)   # AFTER EDITS
regular module execution
<module 'propertymodule' from 'propertymodule.pyc'>
>>> propertymodule.dynval
"property function returns 36 in module 'propertymodule'"

참고 : 다음과 같은 from propertymodule import dynval것은 물론 고정 된 사본을 생성합니다.dynval = someobject.dynval


1

짧은 대답 : 사용 proxy_tools

proxy_tools패키지를 제공하려고 노력하고 @module_property기능을.

다음과 함께 설치됩니다.

pip install proxy_tools

에, Marein의 예 @의 약간의 수정을 사용하여 the_module.py우리가 넣어

from proxy_tools import module_property

@module_property
def thing():
    print(". ", end='')  # Prints ". " on each invocation
    return 'hello'

이제 다른 스크립트에서 할 수 있습니다.

import the_module

print(the_module.thing)
# . hello

예상치 못한 동작

이 솔루션에는 경고가 없습니다. 즉, 문자열the_module.thing아닙니다 ! proxy_tools.Proxy문자열을 모방하도록 특수 메서드가 재정 의 된 개체입니다. 요점을 설명하는 몇 가지 기본 테스트는 다음과 같습니다.

res = the_module.thing
# [No output!!! Evaluation doesn't occur yet.]

print(type(res))
# <class 'proxy_tools.Proxy'>

print(isinstance(res, str))
# False

print(res)
# . hello

print(res + " there")
# . hello there

print(isinstance(res + "", str))
# . True

print(res.split('e'))
# . ['h', 'llo']

내부적으로 원래 함수는 다음에 저장됩니다 the_module.thing._Proxy__local.

print(res._Proxy__local)
# <function thing at 0x7f729c3bf680>

추가 생각

솔직히, 모듈에이 기능이 내장되어 있지 않은 이유에 대해 당황 스럽습니다. 문제의 핵심은 그것이 클래스 the_module의 인스턴스 라고 생각합니다 types.ModuleType. "모듈 속성"을 설정하는 것은 클래스 자체가 아니라이 클래스 의 인스턴스 에 속성을 설정하는 것과 같습니다 types.ModuleType. 자세한 내용은 이 답변을 참조하십시오 .

types.ModuleType결과가 좋지는 않지만 실제로 다음과 같이 속성을 구현할 수 있습니다 . 내장 유형을 직접 수정할 수는 없지만 저주 할 수는 있습니다.

# python -m pip install forbiddenfruit
from forbiddenfruit import curse
from types import ModuleType
# curse has the same signature as setattr.
curse(ModuleType, "thing2", property(lambda module: f'hi from {module.__name__}'))

이것은 우리에게 모든 모듈에 존재하는 속성을 제공합니다. 모든 모듈에서 설정 동작을 깨기 때문에 약간 다루기 어렵습니다.

import sys

print(sys.thing2)
# hi from sys

sys.thing2 = 5
# AttributeError: can't set attribute

1
@Alex Martelli의 답변에 표시된 것처럼 모듈을 실제 클래스의 인스턴스로 만드는 것보다 더 나은 방법은 무엇입니까?
martineau

1
나에게 의미가없는 다른 말을하셨습니다. @module_property데코레이터 를 갖는 것에 대해이 사업을하십시오 . 일반적으로 기본 제공 @property데코레이터는 클래스의 인스턴스가 생성 된 후가 아니라 클래스가 정의 될 때 사용됩니다. 따라서 모듈 속성에 대해서도 동일하다고 가정하고 Alex의 답변과 같습니다. "모듈은 객체와 동일한 방식으로 속성을 가질 수 있습니까?". 그러나 이다 나중에 추가 할 수 나는 내 이전 수정 한 조각을 할 수있는 방법을 설명하기 위해.
martineau

1
Ben : 구체적인 예제에서 코드를 살펴본 후 지금 무엇을 얻고 있는지 이해하는 것 같습니다. 나는 또한 최근에 Alex의 대답과 같은 클래스 인스턴스로 모듈을 대체 할 필요가없는 모듈 속성과 유사한 것을 구현하는 기술을 우연히 발견했다고 생각합니다.이 시점에서 할 방법이 있는지 확실하지 않습니다. 데코레이터를 통해 진행됩니다. 진전이 있으면 다시 연락 드리겠습니다.
martineau

1
좋아요, 여기 핵심 아이디어가 포함 된 다른 질문 에 대한 답변 에 대한 링크가 있습니다.
martineau

1
글쎄, 적어도의 경우 속성이 정의되면 더 이상 호출되지 않는다는 cached_module_property사실 __getattr__()이 도움이됩니다. ( functools.cached_property수행하는 것과 유사 ).
martineau
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.