{ "info": { "author": "Jon Brandvein", "author_email": "jon.brandvein@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "# SimpleStruct #\n\n*(Supports Python 3.3 and up)*\n\nThis small library makes it easier to create \"struct\" classes in Python\nwithout writing boilerplate code. Structs are similar to the standard\nlibrary's [`collections.namedtuple`][1] but are more flexible, relying on an\ninheritance-based approach instead of `eval()`ing a code template. If\nyou like using `namedtuple` classes but wish they were more composable\nand extensible, this project is for you.\n\n## Example ##\n\nWriting struct classes by hand is tedious and error prone. Consider a\nsimple point class. The bare minimum we can write in Python is\n\n```python\nclass Point2D:\n def __init__(self, x, y):\n self.x = x\n self.y = y\n```\n\nWe'll likely want to compare points for equality and pretty-print them\nfor debugging.\n\n```python\nclass Point2D:\n def __init__(self, x, y):\n self.x = x\n self.y = y\n def __repr__(self):\n # Separate __str__() would be nice too\n return 'Point2D({!r}, {!r})'.format(self.x, self.y)\n def __eq__(self, other):\n # Should check other's type too\n return self.x == other.x and self.y == other.y\n def __hash__(self):\n # Required because we're overriding __eq__().\n return hash(self.x) ^ hash(self.y)\n```\n\nAlready the code is becoming pretty verbose for such a simple concept.\nWorse, it violates the [DRY principle](http://en.wikipedia.org/wiki/Don%27t_repeat_yourself)\nin that the `x` and `y` fields appear many times. This isn't very\nrobust. If we decide to turn this into a `Point3D` class, we'll have\nto upgrade each method to accommodate a new z coordinate. We could be\nin for an infuriating bug if we forget to update `__eq__()` or\n`__hash__()`. Adding more utilities like a copy/replace method will\nexacerbate the situation.\n\nThen there's the added code for consistency checking. Maybe you're the\nsort of heathen who prefers dynamic type checking over blindly trusting\nMama Ducktype. Or maybe you want to disallow overwriting `x` and `y` so\nas to avoid changing its hash value. Either way you'd need to use\ndescriptors or properties to intercept writes.\n\nSimpleStruct provides a simple alternative. Here is a `Point2D` class\nthat provides everything described above.\n\n```python\nfrom numbers import Number # standard library abstract base class\nfrom simplestruct import Struct, Field, TypedField\n\nclass Point2D(Struct):\n # Note that field declaration order matters.\n x = TypedField(Number)\n y = TypedField(Number)\n```\n\nOf course, customizations are possible. Type checking is by no means\nrequired, objects may be mutable so long as they are not hashed,\nand you can add your own non-Field attributes and properties.\n\n```python\nclass Point2D(Struct):\n _immutable = False\n x = Field\n y = Field\n \n # magnitude won't be considered when hashing or testing equality\n @property\n def magnitude(self):\n return (self.x**2 + self.y**2) ** .5\n```\n\nFor more usage examples, see the sample files:\n\nFile | Purpose\n---|---\n[point.py](examples/point.py) | introduction, basic use\n[typed.py](examples/typed.py) | type-checked fields\n[vector.py](examples/vector.py) | advanced features\n[abstract.py](examples/abstract.py) | mixing structs and metaclasses\n\n## Comparison and feature matrix ##\n\nThe most important problems mentioned above are solved by using\n`namedtuple`, but this approach begins to break down when you\nstart to customize classes. To add a property to a `namedtuple`,\nyou must define a subclass:\n\n```python\nBasePerson = namedtuple('BasePerson', 'fname lname age')\nclass Person(BasePerson):\n @property\n def full_name(self):\n return self.fname + ' ' + self.lname\n```\n\nIf on the other hand you want to extend an existing `namedtuple` with\nnew fields, it's a bit harder. You need to regenerate (not inherit)\nthe boilerplate methods so they recognize the new fields. This can be\ndone using multiple inheritance:\n\n```python\nBaseEmployee = namedtuple('BaseEmployee', BasePerson._fields + ('salary',))\nclass Employee(BaseEmployee, Person):\n pass\n```\n\nImplementation wise, `namedtuple` works by dynamically evaluating\na templated class definition based on the built-in `tuple` type.\nThis gives it a speed advantage, but is also the main reason why\nit is less extensible (and unable to handle mutable values).\n\nIn contrast, SimpleStruct is based on metaclasses, descriptors, and\ndynamic dispatch. The below matrix summarizes the feature comparison.\n\nFeature | Avoids boilerplate for | Supported by `namedtuple`?\n---|:---:|:---:\neasy construction | `__init__()` | \u2713\nextra attributes on self | | subclasses only\npretty printing | `__str()__`, `__repr()__` | \u2713\nstructural equality | `__eq__()` | \u2713\neasy inheritance | | \u2717\noptional mutability | | \u2717\nhashing (if immutable) | `__hash__()` | \u2713\npickling / deep-copying | | \u2713\ntuple decomposition | `__len__`, `__iter__` | \u2713\nindexing | `__getitem__`, `__setitem__` | `__getitem__` only\noptional type checking | `__init__()`, `@property` | \u2717\n`_asdict()` / `_replace()` | | \u2713\n\n[MacroPy][2]'s \"case classes\" provide similar functionality, but are\nimplemented in a very different way. Instead of metaclass hacking\nor source code templating, MacroPy relies on syntactic transformation\nof the module's AST. This allows for a syntax that's very different\nfrom what we've seen above. So different, in fact, that we might view\nMacroPy as an extension to the Python language rather than as just\na library. Case classes are subject to limitations on\ninheritance and class members.\n\n## Installation ##\n\nAs with most Python packages, SimpleStruct is available on PyPI:\n\n```\npython -m pip install simplestruct\n```\n\nOr grab a development version if you're so inclined:\n\n```\npython -m pip install https://github.com/brandjon/simplestruct/tree/tarball/develop\n```\n\nPython 3.3 and 3.4 are supported. There are no additional dependencies.\n\n## Developers ##\n\nTests can be run with `python setup.py test`, or alternatively by\ninstalling [Tox](http://testrun.org/tox/latest/) and running \n`python -m tox` in the project root. Tox has the advantage of automatically\ntesting under both Python 3.3 and 3.4. Building a source distribution\n(`python setup.py sdist`) requires the setuptools extension package\n[setuptools-git](https://github.com/wichert/setuptools-git).\n\n## References ##\n\n[1]: https://docs.python.org/3/library/collections.html#collections.namedtuple\n[[1]] The standard library's `namedtuple` feature\n\n[2]: https://github.com/lihaoyi/macropy#case-classes\n[[2]] Li Haoyi's case classes (part of MacroPy)\n\n[3]: http://harts.net/reece/2013/06/02/using-namedtuples-with-method-and-instance-variable-inheritance/\n[[3]] Reece Hart's blog post on inheriting from `namedtuple`\n", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/brandjon/simplestruct", "keywords": null, "license": "MIT License", "maintainer": null, "maintainer_email": null, "name": "SimpleStruct", "package_url": "https://pypi.org/project/SimpleStruct/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/SimpleStruct/", "project_urls": { "Download": "UNKNOWN", "Homepage": "https://github.com/brandjon/simplestruct" }, "release_url": "https://pypi.org/project/SimpleStruct/0.2.2/", "requires_dist": null, "requires_python": null, "summary": "A library for defining struct-like classes", "version": "0.2.2" }, "last_serial": 2117260, "releases": { "0.2.0": [ { "comment_text": "", "digests": { "md5": "e90f0a6aef107f1f4b5915600f676d6c", "sha256": "2a971e0b8d3ad7a3faa70d111d9b2db4b6fbe52b1ef73855cebe2d07bdf84851" }, "downloads": -1, "filename": "SimpleStruct-0.2.0.zip", "has_sig": false, "md5_digest": "e90f0a6aef107f1f4b5915600f676d6c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 15612, "upload_time": "2014-12-16T01:18:47", "url": "https://files.pythonhosted.org/packages/eb/42/2cfc725db59c91431fc78a5fa7b4a251e008dedc33d25c43365da5b7b06f/SimpleStruct-0.2.0.zip" } ], "0.2.1": [ { "comment_text": "", "digests": { "md5": "4e57e3167b54183ad9e2535a6f951836", "sha256": "051c2f1ddb4112cdc8db3df873408dcdded41e8478b53bfc0d71f83df88e883b" }, "downloads": -1, "filename": "SimpleStruct-0.2.1.zip", "has_sig": false, "md5_digest": "4e57e3167b54183ad9e2535a6f951836", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 22242, "upload_time": "2014-12-21T03:05:44", "url": "https://files.pythonhosted.org/packages/78/68/d36a51c6166026786063505bd37449be38888cdda51c4042d55a04ea1f33/SimpleStruct-0.2.1.zip" } ], "0.2.2": [ { "comment_text": "", "digests": { "md5": "50f3ba755fef91a3512c3f40a56c18bf", "sha256": "90abf21390843d426be82d4d1da7e14a8b89faac29834e3fe7a06fee13c3d0fd" }, "downloads": -1, "filename": "SimpleStruct-0.2.2.zip", "has_sig": false, "md5_digest": "50f3ba755fef91a3512c3f40a56c18bf", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 23864, "upload_time": "2016-05-15T23:17:26", "url": "https://files.pythonhosted.org/packages/99/8e/967153785f9a9bfe3c9612abb5564263a67d29bc185e8bc644a7a05fa84c/SimpleStruct-0.2.2.zip" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "50f3ba755fef91a3512c3f40a56c18bf", "sha256": "90abf21390843d426be82d4d1da7e14a8b89faac29834e3fe7a06fee13c3d0fd" }, "downloads": -1, "filename": "SimpleStruct-0.2.2.zip", "has_sig": false, "md5_digest": "50f3ba755fef91a3512c3f40a56c18bf", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 23864, "upload_time": "2016-05-15T23:17:26", "url": "https://files.pythonhosted.org/packages/99/8e/967153785f9a9bfe3c9612abb5564263a67d29bc185e8bc644a7a05fa84c/SimpleStruct-0.2.2.zip" } ] }