In Python können Sie Eigenschaften mithilfe des Eigenschaftendekorators wie folgt definieren. Beispielsweise kann eine Benutzerklasse mit den Eigenschaften name und email wie folgt definiert werden:
class User:
def __init__(self, *, name=None, email=None):
self.__name = name
self.__email = email
@property
def name(self):
return self.__name
@name.setter
def name(self, name):
self.__name = name
@property
def email(self):
return self.__email
@email.setter
def email(self, email):
self.__email = email
Sie können den Wert einer Eigenschaft als `user.name``` und die Werteinstellung als`
user.name = "Taro" `` `schreiben.
user = User(name="taro", email="[email protected]")
print(user.name) # => "taro"
user.name = "Taro"
print(user.name) # => "Taro"
Für die Seite, die die Benutzerklasse verwendet, ist nichts ärgerlich, aber aus der Sicht der Seite, die die Benutzerklasse definiert, ist es ärgerlich, weil Sie so viele ähnliche Codes schreiben müssen, wie es Eigenschaften gibt.
Definieren Sie daher die folgende Hilfsfunktion.
def define_property(self, name, value=None):
# "_User__name"Name nach Name Mangling.
field_name = "_{}__{}".format(self.__class__.__name__, name)
#Stellen Sie den Anfangswert ein.
setattr(self, field_name, value)
# getter/Setter generieren,Eigenschaften definieren.
getter = lambda _: getattr(self, field_name)
setter = lambda _, value: setattr(self, field_name, value)
setattr(self.__class__, name, property(getter, setter))
Damit kann die Definition der User-Klasse wie folgt gekürzt werden.
class User:
def __init__(self, *, name=None, email=None):
define_property(self, "name", name)
define_property(self, "email", email)
define_property
Könnte die Codemenge erheblich reduzieren,name
Oderemail
Ich halte das mehrfache Schreiben für überflüssig.
Erstellen Sie daher einen Dekorator für den Konstruktor.
def define_properties(*names):
def decorator(constructor):
def wrapper(self, **kwargs):
for name in names:
define_property(self, name, kwargs.get(name))
constructor(self, **kwargs)
return wrapper
return decorator
define_properties
Sie können die Benutzerklasse wie folgt umschreiben:
class User:
@define_properties("name", "email")
def __init__(self):
pass
Es gibt keine Redundanz. Es wird mit etwas mehr Verbesserung abgeschlossen.
Nehmen Sie die folgenden Änderungen vor, um es ein wenig bequemer zu machen:
`True``` für`
`readable``` an, um es als lesbar zu definieren.`True``` für`
writable``` an, um es als beschreibbar zu definieren.Der geänderte Code lautet wie folgt.
define_property.py
def define_property(self, name, value=None, readable=True, writable=True):
# "_User__name"Name nach Name Mangling.
field_name = "_{}__{}".format(self.__class__.__name__, name)
#Stellen Sie den Anfangswert ein.
setattr(self, field_name, value)
# getter/Setter generieren,Eigenschaften definieren.
getter = (lambda self: getattr(self, field_name)) if readable else None
setter = (lambda self, value: setattr(self, field_name, value)) if writable else None
setattr(self.__class__, name, property(getter, setter))
def define_properties(constructor=None, *, accessible=(), readable=(), writable=()):
if callable(constructor):
def wrapper(self, *args, **kwargs):
for name, value in kwargs.items():
define_property(self, name, value)
constructor(self, *args, **kwargs)
return wrapper
else:
to_set = lambda x: set(x) if any(isinstance(x, type_) for type_ in (set, list, tuple)) else {x}
accessibles = to_set(accessible)
readables = accessibles | to_set(readable)
writables = accessibles | to_set(writable)
def decorator(constructor):
def wrapper(self, *args, **kwargs):
for name in (readables | writables):
readable = name in readables
writable = name in writables
initial_value = kwargs.get(name, None)
define_property(self, name, initial_value, readable, writable)
constructor_kwargs = dict([(key, kwargs[key]) for key in (constructor.__kwdefaults__ or {}) if key in kwargs])
constructor(self, *args, **constructor_kwargs)
return wrapper
return decorator
Der Testcode lautet wie folgt.
define_property_test.rb
import unittest
from define_property import *
class DefinePropertyTest(unittest.TestCase):
def test_initial_value(self):
class User:
def __init__(self, *, name=None, email=None):
define_property(self, "name", name)
define_property(self, "email", email)
user = User()
taro = User(name="taro", email="[email protected]")
self.assertEqual(None, user.name)
self.assertEqual(None, user.email)
self.assertEqual("taro", taro.name)
self.assertEqual("[email protected]", taro.email)
def test_accessor(self):
class User:
def __init__(self, *, name=None):
define_property(self, "name", name)
taro = User(name="taro")
self.assertEqual("taro", taro.name)
taro.name = "Taro"
self.assertEqual("Taro", taro.name)
def test_readable(self):
class User:
def __init__(self, *, name=None, email=None):
define_property(self, "name", name, readable=True)
define_property(self, "email", email, readable=False)
taro = User(name="taro", email="[email protected]")
taro.name
with self.assertRaises(AttributeError):
taro.email
def test_writable(self):
class User:
def __init__(self, *, name=None, email=None):
define_property(self, "name", name, writable=True)
define_property(self, "email", email, writable=False)
taro = User(name="taro", email="[email protected]")
taro.name = "Taro"
with self.assertRaises(AttributeError):
taro.email = "[email protected]"
def test_not_accessible(self):
class User:
def __init__(self, *, name=None, email=None):
define_property(self, "name", name, readable=False)
define_property(self, "email", email, readable=False)
taro = User(name="taro", email="[email protected]")
with self.assertRaises(AttributeError):
taro.name
with self.assertRaises(AttributeError):
taro.email
def test_access_by_other_method(self):
class User:
def __init__(self, *, name=None, email=None):
define_property(self, "name", name)
def get_name(self):
return self.__name
taro = User(name="taro")
self.assertEqual("taro", taro.get_name())
class DefinePropertiesTest(unittest.TestCase):
def test_no_arguments(self):
class User:
@define_properties
def __init__(self, *, name=None, email=None):
pass
with self.assertRaises(AttributeError):
User().name
User(name="taro").name
with self.assertRaises(AttributeError):
User(name="taro").email
User(name="taro", email="[email protected]").email
def test_initial_value(self):
class User:
@define_properties(accessible=("name", "email"))
def __init__(self, *, name=None, email=None):
if name != None:
self.__name = self.name.upper()
if email != None:
self.__email = self.email.upper()
user = User()
self.assertEqual(None, user.name)
self.assertEqual(None, user.email)
taro = User(name="taro", email="[email protected]")
self.assertEqual("TARO", taro.name)
self.assertEqual("[email protected]", taro.email)
def test_accessible_with_no_arguments(self):
class User:
@define_properties(accessible=())
def __init__(self):
pass
user = User()
with self.assertRaises(AttributeError):
user.name
def test_accessible_with_string_argument(self):
class User:
@define_properties(accessible="name")
def __init__(self):
pass
user = User()
self.assertEqual(None, user.name)
user.name = "jiro"
self.assertEqual("jiro", user.name)
user = User(name="taro")
self.assertEqual("taro", user.name)
user.name = "jiro"
self.assertEqual("jiro", user.name)
def test_accessible_with_tuple_argument(self):
class User:
@define_properties(accessible=("name", "email"))
def __init__(self):
pass
user = User()
self.assertEqual(None, user.name)
self.assertEqual(None, user.email)
user.name = "jiro"
user.email = "[email protected]"
self.assertEqual("jiro", user.name)
self.assertEqual("[email protected]", user.email)
user = User(name="taro", email="[email protected]")
self.assertEqual("taro", user.name)
self.assertEqual("[email protected]", user.email)
user.name = "jiro"
user.email = "[email protected]"
self.assertEqual("jiro", user.name)
self.assertEqual("[email protected]", user.email)
def test_readable_with_no_arguments(self):
class User:
@define_properties(readable=())
def __init__(self):
pass
user = User()
with self.assertRaises(AttributeError):
user.name
def test_readable_with_string_argument(self):
class User:
@define_properties(readable="name")
def __init__(self):
pass
user = User()
self.assertEqual(None, user.name)
with self.assertRaises(AttributeError):
user.name = "jiro"
user = User(name="taro")
self.assertEqual("taro", user.name)
with self.assertRaises(AttributeError):
user.name = "jiro"
def test_readable_with_tuple_argument(self):
class User:
@define_properties(readable=("name", "email"))
def __init__(self):
pass
user = User()
self.assertEqual(None, user.name)
self.assertEqual(None, user.email)
with self.assertRaises(AttributeError):
user.name = "jiro"
with self.assertRaises(AttributeError):
user.email = "[email protected]"
user = User(name="taro", email="[email protected]")
self.assertEqual("taro", user.name)
self.assertEqual("[email protected]", user.email)
with self.assertRaises(AttributeError):
user.name = "jiro"
with self.assertRaises(AttributeError):
user.email = "[email protected]"
def test_writable_with_no_arguments(self):
class User:
@define_properties(writable=())
def __init__(self):
pass
user = User()
with self.assertRaises(AttributeError):
user.name
user.name = "taro"
def test_writable_with_string_argument(self):
class User:
@define_properties(writable="name")
def __init__(self):
pass
user = User()
with self.assertRaises(AttributeError):
user.name
user.name = "jiro"
user = User(name="taro")
with self.assertRaises(AttributeError):
user.name
user.name = "jiro"
def test_writable_with_tuple_argument(self):
class User:
@define_properties(writable=("name", "email"))
def __init__(self):
pass
user = User()
with self.assertRaises(AttributeError):
user.name
with self.assertRaises(AttributeError):
user.email
user.name = "jiro"
user.email = "[email protected]"
user = User(name="taro", email="[email protected]")
with self.assertRaises(AttributeError):
user.name
with self.assertRaises(AttributeError):
user.email
user.name = "jiro"
user.email = "[email protected]"
if __name__ == "__main__":
unittest.main()
Recommended Posts