programing

모듈이 개체가 할 수 있는 것과 동일한 속성을 가질 수 있습니까?

testmans 2023. 10. 7. 09:30
반응형

모듈이 개체가 할 수 있는 것과 동일한 속성을 가질 수 있습니까?

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

obj.y 

값만 반환하는 것이 아니라 함수를 호출합니다.

모듈로 이것을 할 수 있는 방법이 있습니까?제가 원하는 케이스가 있습니다.

module.y 

단순히 저장된 값을 반환하는 것이 아니라 함수를 호출합니다.

PEP 562가 Python >= 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.

편집

내 대답에는 세터와 삭제자가 있을 수 없습니다.필요하다면 kxr의 답변을 받아들이세요.

하위 클래스 만들기<class 'module'>, 해당 클래스에서 속성을 정의한 다음 모듈 클래스를 해당 클래스로 변경합니다.

파일: mymodule.py

import sys

class This(sys.__class__):  # sys.__class__ is <class 'module'>
    _y = 3

    @property
    def y(self):          # do the property things in this class
        return self._y

    @y.setter
    def y(self, value):   # setter is also OK
        self._y = value

other = 4

sys.modules[__name__].__class__ = This  # change module class into This

데모:

>>> import mymodule
>>> mymodule.y
3
>>> mymodule.other
4
>>> mymodule.y = 5
>>> mymodule.y
5
>>> mymodule._y
5    # to prove that setter works

저는 너무 초보자라서 왜 작동하는지 알 수가 없습니다.그래서 크레딧은 kxr로 가야 합니다.

새 스타일 클래스의 인스턴스만 속성을 가질 수 있습니다.Python이 이러한 인스턴스를 모듈이라고 믿게 할 수 있습니다.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()

모듈의 모든 속성을 올바르게 상속하고 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

존 린의 답변을 토대로:

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는 속성이 존재하는지 알 수 없으며 빨간색 물결 모양을 표시합니다.

파이썬 3 업데이트

파이썬 3에서는 적어도 3.7 이후로 모듈 클래스를 서브 클래스로 변경할 수 있으므로 실제 모듈 속성(또는 디스크립터)을 구현하기 쉽습니다. PEP 562 모듈보다 견고하고 강력합니다.__getattr__.

# mymodule.py

class ThisMod(sys.modules[__name__].__class__):
    y = property(lambda self: "Hi this is module %s." % __name__)
    const = property(lambda self: _const)  # block setting
sys.modules[__name__].__class__ = ThisMod

_const = 77

# rest of module code ...

파이썬 2 호환

전형적인 사용 사례는 모든 모듈 내용을 클래스 레이아웃으로 전환하지 않고 기존 모듈을 몇 가지(몇 가지) 동적 특성으로 강화하는 것입니다.안타깝게도 가장 간단한 모듈 클래스 패치는 다음과 같습니다.sys.modules[__name__].__class__ = MyPropertyModule실패로 돌아가다TypeError: __class__ assignment: only for heap types. 따라서 모듈 제작은 재배선이 필요합니다.

이 접근 방식은 Python Import Hook 없이도 모듈 코드 위에 프롤로그를 몇 가지만 올려도 가능합니다.

# 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

답 을 사용합니다.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.ModuleTypeclass. "module property"를 설정하는 것은 이 클래스의 인스턴스에 속성을 설정하는 것과 같습니다.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

사용자 2124834의 답변을 토대로:

import sys
class AttrGeter:
    def __new__(cls, gt):
        if isinstance(gt, cls):
            return gt
        else:
            o = super().__new__(cls)
            o.oldgetattr = gt
            o.funcmap = {}
            return o

    def __call__(self, name):
        name2 = "_" + name
        if name2 in self.funcmap:
            return self.funcmap[name2]()
        else:
            return self.oldgetattr(name)

    def add(self, func):
        self.funcmap[func.__name__] = func


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}'")
    ag = AttrGeter(getattr(module, '__getattr__', base_getattr))
    module.__getattr__ = ag
    ag.add(func)
    return func

_module에서 사용(앞줄 밑줄 참조).py:

@module_property
def _thing():
    return 'hello'

그러면:

import the_module

print(the_module.thing)  # prints 'hello'

나는 a를 사용합니다.dictd function원액으로이 더 일 수 .하나의 모듈에서 데코레이터를 여러 번 사용하면 더 효율적일 수 있습니다.

구글은 장식가 한 쌍을 제공한 이 요지로 나를 이끌었습니다.mod_property,cached_mod_property간단한 구현이 가능합니다.제가 시도해봤는데 저를 위해 일을 해주더라고요.

저는 그 요지에서 코드를 가져왔고, 일부 코드는 디킨스에서 가져왔고, 여기서 완전한 데모와 함께 하나의 유틸리티 모듈로 결합했습니다.

매니페스트:

  • 캐시(함수)
  • cashed_property(예: 메서드)
  • 모듈_
  • 캐시된_module_
  • 계급_
  • 캐시된_클래스_

언급URL : https://stackoverflow.com/questions/880530/can-modules-have-properties-the-same-way-that-objects-can

반응형