python [Python 3.6]带类型管理的属性加载器。更多信息@ https://www.reddit.com/r/Python/comments/641dhn/attribute_loade
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python [Python 3.6]带类型管理的属性加载器。更多信息@ https://www.reddit.com/r/Python/comments/641dhn/attribute_loade相关的知识,希望对你有一定的参考价值。
from enum import Enum
import re
###########################################################################################################################
#///// ////////////////////////////////////////////////////////////////////////////////////////////////////////////
#///// Utils ////////////////////////////////////////////////////////////////////////////////////////////////////////////
#///// ////////////////////////////////////////////////////////////////////////////////////////////////////////////
###########################################################################################################################
def _quote_StringOnly(x):
return f"'{x}'" if isinstance(x, str) else(x)
###########################################################################################################################
#///// ///////////////////////////////////////////////////////////////////////////////////////////////////////
#///// Properties ///////////////////////////////////////////////////////////////////////////////////////////////////////
#///// ///////////////////////////////////////////////////////////////////////////////////////////////////////
###########################################################################################################################
class PropertySet:
def __init__(self, *properties):
self._entries = set(properties)
self._keys = [x.key for x in properties]
def __call__(self): return self._entries
def get(self, key): return next((x for x in self._entries if(x.key == key)), None)
def keys(self): return self._keys
class Property:
class _INIT(Enum): PROPERTY = 0
INIT = _INIT.PROPERTY
def __init__(self, type: object, key: str, default: object):
self.type = type
self.key = key
self.default = default
def __repr__(self): return self._to_String()
def __str__(self): return self._to_String()
def _to_String(self): return f"Property{{type={self.type.__name__}, key='{self.key}', default={self.default}}}"
###########################################################################################################################
#///// //////////////////////////////////////////////////////////////////////////////////////////////////////
#///// TypeManaged //////////////////////////////////////////////////////////////////////////////////////////////////////
#///// //////////////////////////////////////////////////////////////////////////////////////////////////////
###########################################################################################################################
class TypeManaged:
_properties: PropertySet
def __init__(self, **attributes):
self._set_Attributes(attributes)
self._validate_Attributes()
def _set_Attributes(self, attributes):
for key, value in attributes.items():
_property = self._properties.get(key)
if (_property is None): raise Undefined_Property(self, key, value)
elif not(isinstance(value, _property.type)): raise Invalid_Assignment(self, key, value)
else: self.__dict__[key] = value
def _validate_Attributes(self):
for _property in self._properties():
validKey = re.compile(r"^[a-z_][a-z0-9_]*$", re.IGNORECASE)
if not(validKey.match(_property.key)): raise Invalid_PropertyKey(self, _property)
if hasattr(self, _property.key): continue
if (_property.default == Property.INIT): raise Uninitialized_Property (self, _property)
elif not(isinstance(_property.default, _property.type)): raise Invalid_PropertyDefault(self, _property)
else: self.__dict__[_property.key] = _property.default
def __setattr__(self, key, value):
if key in self._properties.keys() and not isinstance(value, self._properties.get(key).type):
raise Invalid_Assignment(self, key, value)
object.__setattr__(self, key, value)
###########################################################################################################################
#///// ////////////////////////////////////////////////////////////////////////////////////////////////////////
#///// ErrorData ////////////////////////////////////////////////////////////////////////////////////////////////////////
#///// ////////////////////////////////////////////////////////////////////////////////////////////////////////
###########################################################################################################################
class ErrorData_From_SetAttr:
def __init__(self, classInstance, key, value):
properties = classInstance._properties
self.key = key
self.value = _quote_StringOnly(value)
self.definedType = properties.get(key).type.__name__ if(key in properties.keys()) else(None)
self.actualType = type(value).__name__
self.className = classInstance.__class__.__name__
class ErrorData_From_ValidateAttr:
def __init__(self, classInstance, _property):
self.key = _property.key
self.defaultValue = _quote_StringOnly(_property.default)
self.definedType = _property.type.__name__
self.actualType = type(_property.default).__name__
self.className = classInstance.__class__.__name__
###########################################################################################################################
#///// ///////////////////////////////////////////////////////////////////////////////////////////////////////
#///// Exceptions ///////////////////////////////////////////////////////////////////////////////////////////////////////
#///// ///////////////////////////////////////////////////////////////////////////////////////////////////////
###########################################################################################################################
###########################
### @ _set_Attributes ###
###########################
class Undefined_Property(Exception):
def __init__(self, classInstance, key, value):
e = ErrorData_From_SetAttr(classInstance, key, value)
message = (
f"\n\t>>> {e.className}.{e.key} = ({e.actualType}) {e.value}"
f"\n\t{e.className}.{e.key} is not a defined property"
)
super(Undefined_Property, self).__init__(message)
class Invalid_Assignment(Exception):
def __init__(self, classInstance, key, value):
e = ErrorData_From_SetAttr(classInstance, key, value)
message = (
f"\n\t>>> {e.className}.{e.key} = ({e.actualType}) {e.value}"
f"\n\t{e.className}.{e.key} must be of ({e.definedType}) type"
)
super(Invalid_Assignment, self).__init__(message)
################################
### @ _validate_Attributes ###
################################
class Invalid_PropertyKey(Exception):
def __init__(self, classInstance, _property):
e = ErrorData_From_ValidateAttr(classInstance, _property)
message = (
f"\n\t{e.className}.{e.key} is not a valid key."
f"\n\tKeys must match the following RegEx pattern:"
f"\n\t\t^[a-zA-Z_][a-zA-Z0-9_]*$"
)
super(Invalid_PropertyKey, self).__init__(message)
class Uninitialized_Property(Exception):
def __init__(self, classInstance, _property):
e = ErrorData_From_ValidateAttr(classInstance, _property)
message = f"\n\t{e.className}.{e.key} must be instantiated"
super(Uninitialized_Property, self).__init__(message)
class Invalid_PropertyDefault(Exception):
def __init__(self, classInstance, _property):
e = ErrorData_From_ValidateAttr(classInstance, _property)
message = (
f"\n\t{e.className}.{e.key} requires ({e.definedType}) default value"
f"\n\t({e.actualType}) {e.defaultValue} is not a valid default"
)
super(Invalid_PropertyDefault, self).__init__(message)
from functools import partial as F
from type_managed import TypeManaged, PropertySet, Property
#####################################################################################################
#///// //////////////////////////////////////////////////////////////////////////////////////
#///// Utils //////////////////////////////////////////////////////////////////////////////////////
#///// //////////////////////////////////////////////////////////////////////////////////////
#####################################################################################################
def test_Partials(*partials, dividerLength=51):
def get_Partial_AsString(f):
args = [str(x) for x in f.args]
for key, value in f.keywords.items():
if isinstance(value, str): args.append(f"{key}='" + value.replace("\n", "\\n") + "'")
else: args.append(f"{key}={value}" )
args_String = ", ".join(list(args))
return f"{f.func.__name__}({args_String})"
for i, f in enumerate(partials):
print(">>> " + get_Partial_AsString(f))
try:
f()
except Exception as E:
message = str(E) if str(E).startswith("\n") else f"\n{E}"
print(f"ERROR: {E.__class__.__name__}{message}")
else:
print("PASS")
if(i < len(partials)-1):
print(f"{'-'*dividerLength}")
#####################################################################################################
#///// ///////////////////////////////////////////////////////////////////////////////////////
#///// Test ///////////////////////////////////////////////////////////////////////////////////////
#///// ///////////////////////////////////////////////////////////////////////////////////////
#####################################################################################################
# Property(type, key, default)
# - if Property.INIT is used as the 'default' argument
# and a class is instantiated without an argument for that property,
# the class will throw an Uninitialized_Property exception
# - `object` can be used for the `type` argument
# in order to avoid type checking
class ExampleClass_1(TypeManaged):
_properties = PropertySet(
Property(int, "X", 0 ),
Property(int, "Y", Property.INIT),
Property(object, "Z", True ),
)
class ExampleClass_2(TypeManaged):
_properties = PropertySet(
Property(int, "X", "one"),
)
class ExampleClass_3(TypeManaged):
_properties = PropertySet(
Property(int, "X!", 0),
)
test_Partials(
F(ExampleClass_1, X=1, Y=2, Z=3 ),
F(ExampleClass_1, X=1, Y=2, Z="three"),
F(ExampleClass_1, X="one", Y=2, Z=3 ),
F(ExampleClass_1, A=1, Y=2, Z=3 ),
F(ExampleClass_1, X=1, Z=3 ),
F(ExampleClass_2 ),
F(ExampleClass_3 ),
)
### RESULTS: ###
"""
>>> ExampleClass_1(X=1, Y=2, Z=3)
PASS
---------------------------------------------------
>>> ExampleClass_1(X=1, Y=2, Z='three')
PASS
---------------------------------------------------
>>> ExampleClass_1(X='one', Y=2, Z=3)
ERROR: Invalid_Assignment
>>> ExampleClass_1.X = (str) 'one'
ExampleClass_1.X must be of (int) type
---------------------------------------------------
>>> ExampleClass_1(A=1, Y=2, Z=3)
ERROR: Undefined_Property
>>> ExampleClass_1.A = (int) 1
ExampleClass_1.A is not a defined property
---------------------------------------------------
>>> ExampleClass_1(X=1, Z=3)
ERROR: Uninitialized_Property
ExampleClass_1.Y must be instantiated
---------------------------------------------------
>>> ExampleClass_2()
ERROR: Invalid_PropertyDefault
ExampleClass_2.X requires (int) default value
(str) 'one' is not a valid default
---------------------------------------------------
>>> ExampleClass_3()
ERROR: Invalid_PropertyKey
ExampleClass_3.X! is not a valid key.
Keys must match the following RegEx pattern:
^[a-zA-Z_][a-zA-Z0-9_]*$
"""
以上是关于python [Python 3.6]带类型管理的属性加载器。更多信息@ https://www.reddit.com/r/Python/comments/641dhn/attribute_loade的主要内容,如果未能解决你的问题,请参考以下文章