📄 chap1.txt
字号:
and the easiest way to get the functionality you want is to
specialize an existing class. Under all non-ancient versions of
Python, the standard library provides the pure-Python modules
[UserDict], [UserList], and [UserString] as starting points for
custom datatypes. You can inherit from an appropriate parent
class and specialize (magic) methods as needed. No sample parents
are provided for tuples, ints, floats, and the rest, however.
Under Python 2.2 and above, a better option is available.
"New-style" Python classes let you inherit from the underlying C
implementations of all the Python basic datatypes. Moreover,
these parent classes have become the self-same callable objects
that are used to coerce types and construct objects: `int()`,
`list()`, `unicode()`, and so on. There is a lot of arcana and
subtle profundities that accompanies new-style classes, but you
generally do not need to worry about these. All you need to know
is that a class that inherits from [string] is faster than one
that inherits from [UserString]; likewise for [list] versus
[UserList] and [dict] versus [UserDict] (assuming your scripts
all run on a recent enough version of Python).
Custom datatypes, however, need not specialize full-fledged
implementations. You are free to create classes that implement
"just enough" of the interface of a basic datatype to be used for
a given purpose. Of course, in practice, the reason you would
create such custom datatypes is either because you want them to
contain non-magic methods of their own or because you want them
to implement the magic methods associated with multiple basic
datatypes. For example, below is a custom datatype that can be
passed to the prior 'approx()' function, and that also provides a
(slightly) useful custom method:
>>> class I: # "Fuzzy" integer datatype
... def __init__(self, i): self.i = i
... def __and__(self, i): return self.i & i
... def err_range(self):
... lbound = approx(self.i)
... return "Value: [%d, %d)" % (lbound, lbound+0x0F)
...
>>> i1, i2 = I(29), I(20)
>>> approx(i1), approx(i2)
(16L, 16L)
>>> i2.err_range()
'Value: [16, 31)'
Despite supporting an extra method and being able to get passed
into the 'approx()' function, 'I' is not a very versatile
datatype. If you try to add, or divide, or multiply using
"fuzzy integers," you will raise a 'TypeError'. Since there
is no module called [UserInt], under an older Python version
you would need to implement every needed magic method yourself.
Using new-style classes in Python 2.2+, you could derive a
"fuzzy integer" from the underlying 'int' datatype. A partial
implementation could look like:
>>> class I2(int): # New-style fuzzy integer
... def __add__(self, j):
... vals = map(int, [approx(self), approx(j)])
... k = int.__add__(*vals)
... return I2(int.__add__(k, 0x0F))
... def err_range(self):
... lbound = approx(self)
... return "Value: [%d, %d)" %(lbound,lbound+0x0F)
...
>>> i1, i2 = I2(29), I2(20)
>>> print "i1 =", i1.err_range(),": i2 =", i2.err_range()
i1 = Value: [16, 31) : i2 = Value: [16, 31)
>>> i3 = i1 + i2
>>> print i3, type(i3)
47 <class '__main__.I2'>
Since the new-style class 'int' already supports bitwise-and,
there is no need to implement it again. With new-style classes,
you refer to data values directly with 'self', rather than as an
attribute that holds the data (e.g., 'self.i' in class 'I'). As
well, it is generally unsafe to use syntactic operators within
magic methods that define their operation; for example, I utilize
the '.__add__()' method of the parent 'int' rather than the '+'
operator in the 'I2.__add__()' method.
In practice, you are less likely to want to create number-like
datatypes than you are to emulate container types. But it is
worth understanding just how and why even plain integers are a
fuzzy concept in Python (the fuzziness of the concepts is of a
different sort than the fuzziness of 'I2' integers, though).
Even a function that operates on whole numbers need not operate
on objects of 'IntType' or 'LongType'--just on an object that
satisfies the desired protocols.
TOPIC -- Base Classes for Datatypes
--------------------------------------------------------------------
There are several magic methods that are often useful to define
for -any- custom datatype. In fact, these methods are useful
even for classes that do not really define datatypes (in some
sense, every object is a datatype since it can contain
attribute values, but not every object supports special
syntax such as arithmetic operators and indexing). Not quite
every magic method that you can define is documented in this
book, but most are under the parent datatype each is most
relevant to. Moreover, each new version of Python has
introduced a few additional magic methods; those covered
either have been around for a few versions or are particularly
important.
In documenting class methods of base classes, the same general
conventions are used as for documenting module functions. The
one special convention for these base class methods is the use
of 'self' as the first argument to all methods. Since the name
'self' is purely arbitrary, this convention is less special
than it might appear. For example, both of the following uses
of 'self' are equally legal:
>>> import string
>>> self = 'spam'
>>> object.__repr__(self)
'<str object at 0x12c0a0>'
>>> string.upper(self)
'SPAM'
However, there is usually little reason to use class methods in
place of perfectly good built-in and module functions with the
same purpose. Normally, these methods of datatype classes are
used only in child classes that override the base classes, as
in:
>>> class UpperObject(object):
... def __repr__(self):
... return object.__repr__(self).upper()
...
>>> uo = UpperObject()
>>> print uo
<__MAIN__.UPPEROBJECT OBJECT AT 0X1C2C6C>
=================================================================
BUILTIN -- object : Ancestor class for new-style datatypes
=================================================================
Under Python 2.2+, 'object' has become a base for new-style
classes. Inheriting from 'object' enables a custom class to
use a few new capabilities, such as slots and properties. But
usually if you are interested in creating a custom datatype, it
is better to inherit from a child of 'object', such as 'list',
'float', or 'dict'.
METHODS:
object.__eq__(self, other)
Return a Boolean comparison between 'self' and 'other'.
Determines how a datatype responds to the '==' operator.
The parent class 'object' does not implement '.__eq__()'
since by default object equality means the same thing as
identity (the 'is' operator). A child is free to
implement this in order to affect comparisons.
object.__ne__(self, other)
Return a Boolean comparison between 'self' and 'other'.
Determines how a datatype responds to the '!=' and '<>'
operators. The parent class 'object' does not implement
'.__ne__()' since by default object inequality means the
same thing as nonidentity (the 'is not' operator).
Although it might seem that equality and inequality always
return opposite values, the methods are not explicitly
defined in terms of each other. You could force the
relationship with:
>>> class EQ(object):
... # Abstract parent class for equality classes
... def __eq__(self, o): return not self <> o
... def __ne__(self, o): return not self == o
...
>>> class Comparable(EQ):
... # By def'ing inequlty, get equlty (or vice versa)
... def __ne__(self, other):
... return someComplexComparison(self, other)
object.__nonzero__(self)
Return a Boolean value for an object. Determines how a
datatype responds to the Boolean comparisons 'or', 'and',
and 'not', and to 'if' and 'filter(None,...)' tests. An
object whose '.__nonzero__()' method returns a true value
is itself treated as a true value.
object.__len__(self)
len(object)
Return an integer representing the "length" of the object.
For collection types, this is fairly straightforward--how
many objects are in the collection? Custom types may
change the behavior to some other meaningful value.
object.__repr__(self)
repr(object)
object.__str__(self)
str(object)
Return a string representation of the object 'self'.
Determines how a datatype responds to the `repr()` and
`str()` built-in functions, to the 'print' keyword, and to the
back-tick operator.
Where feasible, it is desirable to have the '.__repr__()'
method return a representation with sufficient information
in it to reconstruct an identical object. The goal here is
to fulfill the equality 'obj==eval(repr(obj))'. In many
cases, however, you cannot encode sufficient information in
a string, and the 'repr()' of an object is either identical
to, or slightly more detailed than, the 'str()'
representation of the same object.
SEE ALSO, [repr], [operator]
=================================================================
BUILTIN -- file : New-style base class for file objects
=================================================================
Under Python 2.2+, it is possible to create a custom file-like
object by inheriting from the built-in class 'file'. In older
Python versions you may only create file-like objects by
defining the methods that define an object as "file-like."
However, even in recent versions of Python, inheritance from
'file' buys you little--if the data contents come from
somewhere other than a native filesystem, you will have to
reimplement every method you wish to support.
Even more than for other object types, what makes an object
file-like is a fuzzy concept. Depending on your purpose you
may be happy with an object that can only read, or one that can
only write. You may need to seek within the object, or you may
be happy with a linear stream. In general, however, file-like
objects are expected to read and write strings. Custom classes
only need implement those methods that are meaningful to them
and should only be used in contexts where their capabilities
are sufficient.
In documenting the methods of file-like objects, I adopt a
slightly different convention than for other built-in types.
Since actually inheriting from 'file' is unusual, I use the
capitalized name 'FILE' to indicate a general file-like object.
Instances of the actual 'file' class are examples (and
implement all the methods named), but other types of objects
can be equally good 'FILE' instances.
BUILT-IN FUNCTIONS:
open(fname [,mode [,buffering]])
file(fname [,mode [,buffering]])
Return a file object that attaches to the filename 'fname'.
The optional argument 'mode' describes the capabilities and
access style of the object. An 'r' mode is for reading;
'w' for writing (truncating any existing content); 'a'
for appending (writing to the end). Each of these modes
may also have the binary flag 'b' for platforms like
Windows that distinguish text and binary files. The flag
'+' may be used to allow both reading and writing. The
argument 'buffering' may be 0 for none, 1 for line-oriented,
a larger integer for number of bytes.
>>> open('tmp','w').write('spam and eggs\n')
>>> print open('tmp','r').read(),
spam and eggs
>>> open('tmp','w').write('this and that\n')
>>> print open('tmp','r').read(),
this and that
>>> open('tmp','a').write('something else\n')
>>> print open('tmp','r').read(),
this and that
something else
METHODS AND ATTRIBUTES:
FILE.close()
Close a file object. Reading and writing are disallowed
after a file is closed.
FILE.closed
Return a Boolean value indicating whether the file has been
closed.
FILE.fileno()
Return a file descriptor number for the file. File-like
objects that do not attach to actual files should not
implement this method.
FILE.flush()
Write any pending data to the underlying file. File-like
objects that do not cache data can still implement this
method as 'pass'.
FILE.isatty()
Return a Boolean value indicating whether the file is a
TTY-like device. The standard documentation says that
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -