Python의 클래스 팩토리
저는 Python을 처음 사용하며 아래 시나리오를 구현하는 몇 가지 조언이 필요합니다.
두 개의 다른 등록 기관에서 도메인을 관리하기위한 두 가지 클래스가 있습니다. 둘 다 동일한 인터페이스를 가지고 있습니다.
class RegistrarA(Object):
def __init__(self, domain):
self.domain = domain
def lookup(self):
...
def register(self, info):
...
과
class RegistrarB(object):
def __init__(self, domain):
self.domain = domain
def lookup(self):
...
def register(self, info):
...
도메인 이름이 주어진 경우 확장을 기반으로 올바른 등록자 클래스를로드하는 도메인 클래스를 만들고 싶습니다.
com = Domain('test.com') #load RegistrarA
com.lookup()
biz = Domain('test.biz') #load RegistrarB
biz.lookup()
공장 기능 (아래 참조)을 사용하여이 작업을 수행 할 수 있다는 것을 알고 있지만 이것이 가장 좋은 방법입니까 아니면 OOP 기능을 사용하는 더 좋은 방법이 있습니까?
def factory(domain):
if ...:
return RegistrarA(domain)
else:
return RegistrarB(domain)
기능을 사용하는 것이 좋다고 생각합니다.
더 흥미로운 질문은로드 할 레지스트라를 어떻게 결정합니까? 한 가지 옵션은 하위 클래스를 구체적으로 구현 한 다음 클래스 메서드 __subclasses__()
호출 을 반복하는 추상 기본 Registrar 클래스를 갖는 is_registrar_for()
것입니다.
class Registrar(object):
def __init__(self, domain):
self.domain = domain
class RegistrarA(Registrar):
@classmethod
def is_registrar_for(cls, domain):
return domain == 'foo.com'
class RegistrarB(Registrar):
@classmethod
def is_registrar_for(cls, domain):
return domain == 'bar.com'
def Domain(domain):
for cls in Registrar.__subclasses__():
if cls.is_registrar_for(domain):
return cls(domain)
raise ValueError
print Domain('foo.com')
print Domain('bar.com')
이렇게하면 새를 투명하게 추가 Registrar
하고 각 도메인이 지원하는 결정을 그들에게 위임 할 수 있습니다.
RegistrarA 와 RegistrarB 는 기능을 공유하고 Abstract Base Class 에서 파생 될 수 있지만 다른 등록 기관에 대해 별도의 클래스가 필요하다고 가정하면 (예에서는 명확하지 않지만) 솔루션이 괜찮아 보입니다 .
factory
함수 의 대안으로 등록자 클래스에 매핑되는 사전을 지정할 수 있습니다.
Registrar = {'test.com': RegistrarA, 'test.biz': RegistrarB}
그때:
registrar = Registrar['test.com'](domain)
한 가지 문제 : 클래스가 아닌 인스턴스를 반환하므로 여기서 클래스 팩토리를 실제로 수행하는 것이 아닙니다.
Python에서는 실제 클래스를 직접 변경할 수 있습니다.
class Domain(object):
def __init__(self, domain):
self.domain = domain
if ...:
self.__class__ = RegistrarA
else:
self.__class__ = RegistrarB
그리고 다음이 작동합니다.
com = Domain('test.com') #load RegistrarA
com.lookup()
이 접근 방식을 성공적으로 사용하고 있습니다.
'래퍼'클래스를 만들고 __new__()
메서드를 오버로드 하여 특수 하위 클래스의 인스턴스를 반환 할 수 있습니다. 예 :
class Registrar(object):
def __new__(self, domain):
if ...:
return RegistrarA(domain)
elif ...:
return RegistrarB(domain)
else:
raise Exception()
또한 다른 답변에서 제기 된 문제인 상호 배타적이지 않은 조건을 처리하기 위해 자신에게 가장 먼저 물어볼 질문은 디스패처 역할을하는 래퍼 클래스가 조건을 제어 할 것인지, 아니면 전문 클래스에 위임합니다. 전문 클래스가 자신의 조건을 정의하는 공유 메커니즘을 제안 할 수 있지만 래퍼는 이와 같이 유효성 검사를 수행합니다 (각 특수 클래스가 특정 도메인에 대한 등록자인지 여부를 확인하는 클래스 메서드 is_registrar_for (. ..) 다른 답변에서 제안) :
class Registrar(object):
registrars = [RegistrarA, RegistrarB]
def __new__(self, domain):
matched_registrars = [r for r in self.registrars if r.is_registrar_for(domain)]
if len(matched_registrars) > 1:
raise Exception('More than one registrar matched!')
elif len(matched_registrars) < 1:
raise Exception('No registrar was matched!')
else:
return matched_registrars[0](domain)
나는 항상이 문제가 있습니다. 응용 프로그램 (및 해당 모듈)에 포함 된 클래스가있는 경우 함수를 사용할 수 있습니다. 그러나 플러그인을 동적으로로드하는 경우 메타 클래스를 통해 자동으로 클래스를 팩토리에 등록하는보다 동적 인 것이 필요합니다.
다음은 원래 StackOverflow에서 해제했다고 확신하는 패턴이지만 여전히 원래 게시물에 대한 경로가 없습니다.
_registry = {}
class PluginType(type):
def __init__(cls, name, bases, attrs):
_registry[name] = cls
return super(PluginType, cls).__init__(name, bases, attrs)
class Plugin(object):
__metaclass__ = PluginType # python <3.0 only
def __init__(self, *args):
pass
def load_class(plugin_name, plugin_dir):
plugin_file = plugin_name + ".py"
for root, dirs, files in os.walk(plugin_dir) :
if plugin_file in (s for s in files if s.endswith('.py')) :
fp, pathname, description = imp.find_module(plugin_name, [root])
try:
mod = imp.load_module(plugin_name, fp, pathname, description)
finally:
if fp:
fp.close()
return
def get_class(plugin_name) :
t = None
if plugin_name in _registry:
t = _registry[plugin_name]
return t
def get_instance(plugin_name, *args):
return get_class(plugin_name)(*args)
어때?
class Domain(object):
registrars = []
@classmethod
def add_registrar( cls, reg ):
registrars.append( reg )
def __init__( self, domain ):
self.domain = domain
for reg in self.__class__.registrars:
if reg.is_registrar_for( domain ):
self.registrar = reg
def lookup( self ):
return self.registrar.lookup()
Domain.add_registrar( RegistrarA )
Domain.add_registrar( RegistrarB )
com = Domain('test.com')
com.lookup()
여기서 메타 클래스는 ENTITIES dict 에서 Registars 클래스를 암시 적으로 수집합니다.
class DomainMeta(type):
ENTITIES = {}
def __new__(cls, name, bases, attrs):
cls = type.__new__(cls, name, bases, attrs)
try:
entity = attrs['domain']
cls.ENTITIES[entity] = cls
except KeyError:
pass
return cls
class Domain(metaclass=DomainMeta):
@classmethod
def factory(cls, domain):
return DomainMeta.ENTITIES[domain]()
class RegistrarA(Domain):
domain = 'test.com'
def lookup(self):
return 'Custom command for .com TLD'
class RegistrarB(Domain):
domain = 'test.biz'
def lookup(self):
return 'Custom command for .biz TLD'
com = Domain.factory('test.com')
type(com) # <class '__main__.RegistrarA'>
com.lookup() # 'Custom command for .com TLD'
com = Domain.factory('test.biz')
type(com) # <class '__main__.RegistrarB'>
com.lookup() # 'Custom command for .biz TLD'
참고 URL : https://stackoverflow.com/questions/456672/class-factory-in-python
'IT TIP' 카테고리의 다른 글
Swagger UI의 파일 문제에서 읽을 수 없음 (0) | 2020.11.20 |
---|---|
pip 캐시 폴더는 어디에 있습니까? (0) | 2020.11.20 |
단순 유형에 속성 추가 또는 XML 스키마의 복합 유형에 제한 (0) | 2020.11.20 |
.NET 콘솔 응용 프로그램 종료 이벤트 (0) | 2020.11.20 |
'실시간'프로세스 우선 순위 설정은 무엇입니까? (0) | 2020.11.20 |