Skip to content

Instantly share code, notes, and snippets.

@JoaoFelipe
Created February 6, 2021 23:01
Show Gist options
  • Save JoaoFelipe/3c146bda9ddd53d0b85a4aad75760da7 to your computer and use it in GitHub Desktop.
Save JoaoFelipe/3c146bda9ddd53d0b85a4aad75760da7 to your computer and use it in GitHub Desktop.
Metaclass for private attributes
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "modern-laundry",
"metadata": {},
"outputs": [],
"source": [
"import types\n",
"from functools import wraps\n",
"from copy import copy\n",
"\n",
"def private_context(method):\n",
" @wraps(method)\n",
" def wrapper(self, *args, **kwargs):\n",
" try:\n",
" object.__setattr__(self, '__private_context__', True)\n",
" return method(self, *args, **kwargs)\n",
" finally:\n",
" object.__setattr__(self, '__private_context__', False)\n",
" return wrapper\n",
"\n",
"\n",
"class PrivateProperty(property):\n",
" \n",
" def __init__(self, prop):\n",
" fget = private_context(prop.fget) if prop.fget else None\n",
" fset = private_context(prop.fset) if prop.fset else None\n",
" fdel = private_context(prop.fdel) if prop.fdel else None\n",
" doc = prop.__doc__\n",
" \n",
" super().__init__(fget, fset, fdel, doc)\n",
" \n",
" \n",
"class MetaPrivate(type):\n",
" \"\"\"Allows the definition of private methods\n",
" It has two modes of operation:\n",
" If you set __private__ as a list of private attributes, \n",
" the only private methods and attributes are the ones in the list\n",
" If you set __public__ as a list of public attributes, \n",
" all attributes and methods become private, except the ones in the list\n",
" \"\"\"\n",
" def __new__(cls, name, bases, dct):\n",
" if '__private__' in dct or '__public__' in dct:\n",
" private = set(dct.get('__private__', []))\n",
" public = set(dct.get('__public__', []))\n",
" new_dct = copy(dct)\n",
" def accessible_key(self, key):\n",
" is_public = key in public\n",
" is_private = '__public__' in dct or key in private\n",
" in_private_context = object.__getattribute__(self, '__private_context__')\n",
" return is_public or not is_private or in_private_context\n",
" def __getattribute__(self, key):\n",
" if accessible_key(self, key):\n",
" return object.__getattribute__(self, key)\n",
" raise AttributeError(f'Attempt to access private attribute \"{key}\"')\n",
" def __setattr__(self, key, value):\n",
" if accessible_key(self, key):\n",
" return object.__setattr__(self, key, value)\n",
" raise AttributeError(f'Attempt to set private attribute \"{key}\"')\n",
" \n",
" new_dct['__private_context__'] = False\n",
" new_dct['__getattribute__'] = __getattribute__\n",
" new_dct['__setattr__'] = __setattr__\n",
" \n",
" for key, value in dct.items():\n",
" if isinstance(value, types.FunctionType):\n",
" new_dct[key] = private_context(value)\n",
" if isinstance(value, property):\n",
" new_dct[key] = PrivateProperty(value)\n",
" \n",
" dct = new_dct\n",
" result = super().__new__(cls, name, bases, dct)\n",
" return result\n",
" "
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "aquatic-killing",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"MyShyPrivate\n",
"spam\n",
"Falhou ao tentar acessar o atributo \"private\"\n",
"Falhou ao tentar criar o atributo \"private\"\n",
"eggs\n",
"MyShyPublic\n",
"spam\n",
"Falhou ao tentar acessar o atributo \"private\"\n",
"Falhou ao tentar criar o atributo \"private\"\n",
"eggs\n"
]
}
],
"source": [
"def exemplo1(cls):\n",
" print(cls.__name__)\n",
" # Uma instência envergonhada.\n",
" my_shy = cls()\n",
"\n",
" # Ok, mostre o valor do seu atributo \"private\".\n",
" my_shy.show_private()\n",
"\n",
" # Mas podemos olhar diretamente o seu atributo \"private\"?\n",
" try:\n",
" # Erro de atributo: para código externo, my_shy não tem atributo \"private\".\n",
" print(my_shy.private)\n",
"\n",
" except AttributeError:\n",
" print('Falhou ao tentar acessar o atributo \"private\"')\n",
"\n",
" # Então podemos criar em você um atributo \"private\"?\n",
" try:\n",
" # Erro de atributo: código externo não pode criar atributos em my_shy.\n",
" my_shy.private = \"eggs\"\n",
"\n",
" except AttributeError:\n",
" print('Falhou ao tentar criar o atributo \"private\"')\n",
"\n",
" # Ok, altere você mesmo o valor do seu atributo \"private\".\n",
" my_shy.set_private()\n",
"\n",
" # Agora, mostre o novo valor do seu atributo \"private\".\n",
" my_shy.show_private()\n",
"\n",
"class MyShyPrivate(metaclass=MetaPrivate):\n",
" __private__ = [\"private\"]\n",
" \n",
" def __init__(self):\n",
" self.private = \"spam\"\n",
" \n",
" def show_private(self):\n",
" print(self.private)\n",
" \n",
" def set_private(self):\n",
" self.private = \"eggs\"\n",
" \n",
"exemplo1(MyShyPrivate)\n",
"\n",
"class MyShyPublic(metaclass=MetaPrivate):\n",
" __public__ = [\"show_private\", \"set_private\"]\n",
" \n",
" def __init__(self):\n",
" self.private = \"spam\"\n",
" \n",
" def show_private(self):\n",
" print(self.private)\n",
" \n",
" def set_private(self):\n",
" self.private = \"eggs\"\n",
"\n",
"exemplo1(MyShyPublic)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "musical-merchandise",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"MyShyPrivate\n",
"spam\n",
"Falhou ao tentar criar o atributo \"read_only\"\n",
"Falhou ao tentar acessar o atributo interno \"data\"\n",
"MyShyPublic\n",
"spam\n",
"Falhou ao tentar criar o atributo \"read_only\"\n",
"Falhou ao tentar acessar o atributo interno \"data\"\n"
]
}
],
"source": [
"def exemplo2(cls):\n",
" print(cls.__name__)\n",
" # Uma instência envergonhada.\n",
" my_shy = cls()\n",
"\n",
" # Podemos olhar diretamente o seu atributo \"read_only\"?\n",
" print(my_shy.read_only)\n",
"\n",
" # Mas podemos alterar o seu atributo \"read_only\"?\n",
" try:\n",
" # Erro de atributo: código externo não pode criar atributos em my_shy.\n",
" my_shy.read_only = \"eggs\"\n",
"\n",
" except AttributeError:\n",
" print('Falhou ao tentar criar o atributo \"read_only\"')\n",
"\n",
" # Mas podemos olhar diretamente o seu atributo interno \"data\"?\n",
" try:\n",
" # Erro de atributo: para código externo, my_shy não tem atributo \"data\".\n",
" print(my_shy.data)\n",
"\n",
" except AttributeError:\n",
" print('Falhou ao tentar acessar o atributo interno \"data\"')\n",
"\n",
"\n",
"class MyShyPrivate(metaclass=MetaPrivate):\n",
" __private__ = ['data']\n",
"\n",
" def __init__(self):\n",
" self.data = \"spam\"\n",
" \n",
" @property\n",
" def read_only(self):\n",
" return self.data\n",
"\n",
"exemplo2(MyShyPrivate)\n",
"\n",
"\n",
"class MyShyPublic(metaclass=MetaPrivate):\n",
" __public__ = ['read_only']\n",
"\n",
" def __init__(self):\n",
" self.data = \"spam\"\n",
" \n",
" @property\n",
" def read_only(self):\n",
" return self.data\n",
"\n",
"exemplo2(MyShyPublic)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "complimentary-bachelor",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"MyShyPrivate\n",
"spam\n",
"eggs\n",
"Falhou ao tentar acessar o atributo interno \"data\"\n",
"MyShyPublic\n",
"spam\n",
"eggs\n",
"Falhou ao tentar acessar o atributo interno \"data\"\n"
]
}
],
"source": [
"def exemplo3(cls):\n",
" print(cls.__name__)\n",
" # Uma instência envergonhada.\n",
" my_shy = cls()\n",
"\n",
" # Podemos olhar diretamente o seu atributo \"read_write\"?\n",
" print(my_shy.read_write)\n",
"\n",
" # Podemos alterar o seu atributo \"read_write\"?\n",
" my_shy.read_write = \"eggs\"\n",
"\n",
" # Ok, mostre o novo valor de \"read_write\".\n",
" print(my_shy.read_write)\n",
"\n",
" # Mas podemos olhar diretamente o seu atributo interno \"data\"?\n",
" try:\n",
" # Erro de atributo: para código externo, my_shy não tem atributo \"data\".\n",
" print(my_shy.data)\n",
"\n",
" except AttributeError:\n",
" print('Falhou ao tentar acessar o atributo interno \"data\"')\n",
"\n",
"\n",
"class MyShyPrivate(metaclass=MetaPrivate):\n",
" __private__ = ['data']\n",
"\n",
" def __init__(self):\n",
" self.data = \"spam\"\n",
" \n",
" @property\n",
" def read_write(self):\n",
" return self.data\n",
" \n",
" @read_write.setter\n",
" def read_write(self, value):\n",
" self.data = value\n",
"\n",
"exemplo3(MyShyPrivate)\n",
"\n",
"\n",
"class MyShyPublic(metaclass=MetaPrivate):\n",
" __public__ = ['read_write']\n",
"\n",
" def __init__(self):\n",
" self.data = \"spam\"\n",
" \n",
" @property\n",
" def read_write(self):\n",
" return self.data\n",
" \n",
" @read_write.setter\n",
" def read_write(self, value):\n",
" self.data = value\n",
"\n",
"exemplo3(MyShyPublic)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "infectious-rocket",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.7"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment