{"id":28210357,"url":"https://github.com/fernandoenzo/private-attrs","last_synced_at":"2025-07-30T04:09:05.964Z","repository":{"id":57454840,"uuid":"264498967","full_name":"fernandoenzo/private-attrs","owner":"fernandoenzo","description":"This module provides support for easy addition of private attributes inside your custom objects, which are totally unreachable from outside the class definition, as in C++ private clause.","archived":false,"fork":false,"pushed_at":"2022-02-12T20:15:58.000Z","size":37,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-19T20:46:08.298Z","etag":null,"topics":["attr","attribute","library","module","package","pip","private","pypi","python","static"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fernandoenzo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-05-16T18:18:15.000Z","updated_at":"2022-08-12T07:57:01.000Z","dependencies_parsed_at":"2022-09-05T05:11:25.181Z","dependency_job_id":null,"html_url":"https://github.com/fernandoenzo/private-attrs","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/fernandoenzo/private-attrs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fernandoenzo%2Fprivate-attrs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fernandoenzo%2Fprivate-attrs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fernandoenzo%2Fprivate-attrs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fernandoenzo%2Fprivate-attrs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fernandoenzo","download_url":"https://codeload.github.com/fernandoenzo/private-attrs/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fernandoenzo%2Fprivate-attrs/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266789310,"owners_count":23984269,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-24T02:00:09.469Z","response_time":99,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["attr","attribute","library","module","package","pip","private","pypi","python","static"],"created_at":"2025-05-17T17:09:52.050Z","updated_at":"2025-07-30T04:09:05.888Z","avatar_url":"https://github.com/fernandoenzo.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# private-attrs\n\n[![PyPI](https://img.shields.io/pypi/v/private-attrs?label=latest)](https://pypi.org/project/private-attrs/)\n![PyPI - Downloads](https://img.shields.io/pypi/dm/private-attrs)\n![PyPI - Python Version](https://img.shields.io/pypi/pyversions/private-attrs)\n![PyPI - Status](https://img.shields.io/pypi/status/private-attrs)\n\n![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/fernandoenzo/private-attrs)\n[![GitHub last commit](https://img.shields.io/github/last-commit/fernandoenzo/private-attrs)](https://github.com/fernandoenzo/private-attrs)\n[![Build Status](https://img.shields.io/travis/com/fernandoenzo/private-attrs?label=tests)](https://travis-ci.com/fernandoenzo/private-attrs)\n![Maintenance](https://img.shields.io/maintenance/yes/2022)\n\nThis little library, consisting of a single module, provides support for easy addition of **truly private attributes**\n inside classes, which are totally unreachable from outside the class definition, **as in C++ private clause**.\n\n## Table of contents\n\n\u003c!--ts--\u003e\n  * [Installation](#installation)\n  * [Usage](#usage)\n      * [A simple example](#a-simple-example)\n      * [An example with proxy=True](#an-example-with-proxytrue)\n  * [A word about compiling](#a-word-about-compiling)\n  * [Contributing](#contributing)\n  * [License](#license)\n\u003c!--te--\u003e\n\n## Installation\n\nUse the package manager [**pip**](https://pip.pypa.io/en/stable/) to install **private-attrs**.\n\n```bash\npip3 install private-attrs\n```\n\n## Usage\n\nThis is a simple schema on how a custom class could use private attributes.\n```python\nfrom private_attrs import PrivateAttrs\n\ndef MyClass():\n    p = PrivateAttrs()\n\n    class MyClass:\n        def __init__(self):\n            p.register_instance(self)\n            \n        # From now on, we'll define our public attrs with 'self.attr' syntax as usual,\n        # and private ones with 'p.attr' or 'p.attr_static'.\n\n        def __del__(self):\n            p.delete(self)\n\n    MyClass.__qualname__ = 'MyClass'    \n\n    return MyClass\n\nMyClass = MyClass()  # override the function definition\n```\nAs you can see, we first need to define the class inside a function. Outside that class, but inside the function scope, we\n instantiate a `PrivateAttrs` object.  \n\nNow, inside `MyClass`, if we plan to have private instance attributes, and not just static ones, it's mandatory to\n register, in the `__init__()` method, the instance by calling the `register_instance()` function.\n\nFinally we return `MyClass` and we override the function definition.\n- - -\n\n#### A simple example\n\nLet's now dive into a more complete example:\n\n```python\nfrom private_attrs import PrivateAttrs\n\ndef Person():\n    p = PrivateAttrs()\n\n    class Person:\n        def __init__(self, name, social_security_number):\n            p.register_instance(self)\n            self.name = name\n            p.ssn = social_security_number\n\n        @property\n        def ssn(self):\n            return p.ssn\n\n        def __eq__(self, other):\n            return self.ssn == other.ssn\n\n        def __hash__(self):\n            return hash(self.ssn)\n\n        def __str__(self):\n            return f\"{self.name} - {self.ssn}\"\n\n        def __del__(self):\n            p.delete(self)\n\n    Person.__qualname__ = 'Person'\n\n    return Person\n\nPerson = Person()\n```\nAlthough a person can change their name, surname or even their sex, it's really unlikely (not to say impossible) for someone\n to change their social security number (SSN).\n\nThat's why we store the SSN as a private attribute, safe, unmodifiable, and we can rely on it to compare whether two people\n are the same person.\n- - -\n\n\n#### An example with proxy=True\n\nIf we are working with the Python `multiprocessing` library and we want to create a class with private attributes that are\n accessible and modifiable from different running processes (we already know that, unlike threads, processes don't share\n  memory space), we need to instantiate the `PrivateAttrs` object with the argument `proxy = True`.  \n\nLet's see an example:\n\n```python\nfrom private_attrs import PrivateAttrs\n\n\ndef Person():\n    p = PrivateAttrs(proxy=True)\n\n    class Person:\n        def __init__(self, name, social_security_number):\n            p.register_instance(self)\n            self.name = name\n            p.cell_phones = p.manager.list()\n            p.ssn = social_security_number\n\n        @property\n        def ssn(self):\n            return p.ssn\n\n        @property\n        def cell_phones(self):\n            return tuple(p.cell_phones)\n\n        def add_cell_phone(self, phone):\n            p.cell_phones.append(phone)\n\n        def __str__(self):\n            return f\"{self.name} - {self.ssn} - {self.cell_phones}\"\n\n        def __del__(self):\n            p.delete(self)\n\n        def __getstate__(self):\n            state = dict(self.__dict__)\n            state['private'] = p.getstate(self)\n            return state\n\n        def __setstate__(self, state):\n            private = state.pop('private')\n            p.setstate(private, self)\n            self.__dict__ = state\n\n    Person.__qualname__ = 'Person'    \n\n    return Person\n\nPerson = Person()\n```\n\nBy doing this, all the private attributes that we store are automatically available in all processes, and you can modify\n them from anyone.\n\nPay particular attention to certain specific attributes that need to be instantiated using the `Manager` class, such as\n lists or dictionaries. Fortunately, there is an attached manager object in the `PrivateAttrs` class to simplify life for\n  the programmer.\n \nAlso be aware of the need to define `__getstate__()` and `__setstate__()` magic methods as you see them so the class can be\n correctly serialized and deserialized with all its private attributes when shared between processes.\n\nYou should know that, the way we wrote this `Person` class, it's impossible for other processes to modify the public \n `name` attribute and make that change visible for the rest. This is because this attribute has not been instantiated with\n  `Manager.Value()` nor inside a `Manager.Namespace()` or similar.\n\nOne possible workaround if you don't want to use the mentioned methods for storing shared simple attributes like `str` or\n `int` would be to make them private and then make a getter (`@property`) and a setter for each one. So the former\n  `Person` class would look like this:\n\n```python\nclass Person:\n    def __init__(self, name, social_security_number):\n        p.register_instance(self)\n        p.name = name\n        p.cell_phones = p.manager.list()\n        p.ssn = social_security_number\n\n    @property\n    def name(self):\n        return p.name\n\n    @name.setter\n    def name(self, name):\n        p.name = name\n\n    @property\n    def ssn(self):\n        return p.ssn\n\n    @property\n    def cell_phones(self):\n        return tuple(p.cell_phones)\n\n    def add_cell_phone(self, phone):\n        p.cell_phones.append(phone)\n\n    def __str__(self):\n        return f\"{self.name} - {self.ssn} - {self.cell_phones}\"\n\n    def __del__(self):\n        p.delete(self)\n\n    def __getstate__(self):\n        state = dict(self.__dict__)\n        state['private'] = p.getstate(self)\n        return state\n\n    def __setstate__(self, state):\n        private = state.pop('private')\n        p.setstate(self, private)\n        self.__dict__ = state\n```\n\n## A word about compiling\n\nIf you plan to compile your program or library using a tool like Cython, Nuitka or similar, you should know that\n private-attrs uses the Python `inspect` module to provide the developer with a friendly interface, which does not\n behave as usual when the program is compiled to C. All you have to do to avoid compilation problems is to use the\n explicit private attribute getter and setter instead of the implicit declarations, only for non-static private attributes.\n\nFor example, replace this:\n\n```python\nclass Person:\n    \n    @property\n    def name(self):\n        return p.name\n\n    @name.setter\n    def name(self, name):\n        p.name = name\n```\n\nFor this\n\n```python\nclass Person:\n    \n    @property\n    def name(self):\n        return p.get_private_attr(self, 'name')\n\n    @name.setter\n    def name(self, name):\n        p.set_private_attr(self, 'name', name)\n```\n\n## Contributing\n\nPull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.\n\n## License\n\n![PyPI - License](https://img.shields.io/pypi/l/private-attrs)\n\nThis library is licensed under the\n [GNU General Public License v3 or later (GPLv3+)](https://choosealicense.com/licenses/gpl-3.0/)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffernandoenzo%2Fprivate-attrs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffernandoenzo%2Fprivate-attrs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffernandoenzo%2Fprivate-attrs/lists"}