Python Cookbook 2Nd Edition Jun 1002005 [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Python Cookbook 2Nd Edition Jun 1002005 [Electronic resources] - نسخه متنی

David Ascher, Alex Martelli, Anna Ravenscroft

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید







Recipe 6.2. Defining Constants


Credit: Alex Martelli


Problem



You
need to define module-level variables (i.e., named constants) that
client code cannot accidentally rebind.


Solution


You can install any object as if it were a module. Save the following
code as module const.py on some directory on
your Python sys.path:

class _const(object):
class ConstError(TypeError): pass
def _ _setattr_ _(self, name, value):
if name in self._ _dict_ _:
raise self.ConstError, "Can't rebind const(%s)" % name
self._ _dict_ _[name] = value
def _ _delattr_ _(self, name):
if name in self._ _dict_ _:
raise self.ConstError, "Can't unbind const(%s)" % name
raise NameError, name
import sys
sys.modules[_ _name_ _] = _const( )

Now, any client code can import const, then bind an
attribute on the const module just once, as follows:

const.magic = 23

Once the attribute is bound, the program cannot accidentally rebind
or unbind it:

const.magic = 88      # raises const.ConstError
del const.magic # raises const.ConstError


Discussion


In Python, variables can be rebound at will, and modules, differently
from classes, don't let you define special methods
such as _ _setattr_ _ to stop rebinding. An easy
solution is to install an instance as if it were a module.

Python performs no type-checks to force entries in
sys.modules to actually be module objects.
Therefore, you can install any object there and take advantage of
attribute-access special methods (e.g., to prevent rebinding, to
synthesize attributes on the fly in _ _getattr_ _,
etc.), while still allowing client code to access the object with
import somename. You
may even see it as a more Pythonic Singleton-style idiom (but see
Recipe 6.16).

This recipe
ensures that a module-level name remains constantly bound to the same
object once it has first been bound to it. This recipe does not deal
with a certain object's immutability, which is quite
a different issue. Altering an object and rebinding a name are
different concepts, as explained in Recipe 4.1. Numbers, strings, and
tuples are immutable: if you bind a name in const to
such an object, not only will the name always be bound to that
object, but the object's contents also will always
be the same since the object is immutable. However, other objects,
such as lists and dictionaries, are mutable: if you bind a name in
const to, say, a list object, the name will always
remain bound to that list object, but the contents of the list may
change (e.g., items in it may be rebound or unbound, more items can
be added with the object's append
method, etc.).

To make "read-only" wrappers around
mutable objects, see Recipe 6.5. You might choose to have
class _const's _ _setattr_
_
method perform such wrapping implicitly. Say you have
saved the code from Recipe 6.5 as module
ro.py somewhere along your Python
sys.path. Then, you need to add, at the start of
module const.py:

import ro

and change the assignment self._ _dict_ _[name] =
value
, used in class
_const's _ _setattr_
_
method to:

    self._ _dict_ _[name] = ro.Readonly(value)

Now, when you set an attribute in const to some
value, what gets bound there is a read-only wrapper to that value.
The underlying value might still get changed by calling mutators on
some other reference to that same value (object), but it cannot be
accidentally changed through the attribute of
"pseudo-module"
const. If you want to avoid such
"accidental changes through other
references", you need to take a copy, as explained
in Recipe 4.1, so that
there exist no other references to the value held by the read-only
wrapper. Ensure that at the start of module
const.py you have:

import ro, copy

and change the assignment in class
_const's _ _setattr_
_
method to:

    self._ _dict_ _[name] = ro.Readonly(copy.copy(value))

If you're sufficiently paranoid, you might even use
copy.deepcopy rather than plain
copy.copy in this latest snippet. However, you may
end up paying substantial amounts of memory, as well as losing some
performance, by these kinds of excessive precautions. You should
evaluate carefully whether so much prudence is really necessary for
your specific application. Whatever you end up deciding about this
issue, Python offers all the tools you need to implement exactly the
amount of constantness you require.

The _const class
presented in this recipe can be seen, in a sense, as the
"complement" of the
NoNewAttrs class, which is presented next in Recipe 6.3. This one ensures that
already bound attributes can never be rebound but lets you freely
bind new attributes; the other one, conversely, lets you freely
rebind attributes that are already bound but blocks the binding of
any new attribute.


See Also


Recipe 6.5; Recipe 6.13; Recipe 4.1; Library
Reference
and Python in a Nutshell
docs on module objects, the import statement, and
the modules attribute of the
sys built-in module.


/ 394