Python Descriptors: Understanding and Using the Descriptor Protocol 1st Edition Jacob Zimmerman download
Python Descriptors: Understanding and Using the Descriptor Protocol 1st Edition Jacob Zimmerman download
https://textbookfull.com/product/python-descriptors-
understanding-and-using-the-descriptor-protocol-1st-edition-
jacob-zimmerman/
https://textbookfull.com/product/python-descriptors-1st-edition-
jacob-zimmerman/
https://textbookfull.com/product/using-asyncio-in-python-
understanding-python-s-asynchronous-programming-features-caleb-
hattingh/
https://textbookfull.com/product/introduction-to-computation-and-
programming-using-python-with-application-to-understanding-data-
guttag/
https://textbookfull.com/product/introduction-to-computation-and-
programming-using-python-with-application-to-understanding-data-
second-edition-john-v-guttag/
Supervised Learning with Python Concepts and Practical
Implementation Using Python 1st Edition Vaibhav Verdhan
https://textbookfull.com/product/supervised-learning-with-python-
concepts-and-practical-implementation-using-python-1st-edition-
vaibhav-verdhan/
https://textbookfull.com/product/supervised-learning-with-python-
concepts-and-practical-implementation-using-python-vaibhav-
verdhan/
https://textbookfull.com/product/chemical-and-biomedical-
engineering-calculations-using-python-1st-edition-jeffrey-j-heys/
https://textbookfull.com/product/computational-nuclear-
engineering-and-radiological-science-using-python-1st-edition-
ryan-mcclarren/
https://textbookfull.com/product/introduction-to-computing-and-
problem-solving-using-python-1st-edition-e-balaguruswamy/
Python
Descriptors
Understanding and Using the
Descriptor Protocol
—
Second Edition
—
Jacob Zimmerman
Python Descriptors
Understanding and Using
the Descriptor Protocol
Second Edition
Jacob Zimmerman
Python Descriptors: Understanding and Using the Descriptor Protocol
Jacob Zimmerman
New York, USA
iii
Table of Contents
iv
Table of Contents
Chapter 9: Writing__delete__()�����������������������������������������������������������67
Summary������������������������������������������������������������������������������������������������������������68
v
Table of Contents
Bibliography���������������������������������������������������������������������������������������93
Index���������������������������������������������������������������������������������������������������95
vi
About the Author
Jacob Zimmerman is a blogger, gamer (tabletop more so than video
games), and programmer who was born and raised in Wisconsin. He has a
twin brother who could also be considered to have all those traits.
Jacob has his own programming blog that focuses on Java, Kotlin,
and Python programming, called “Programming Ideas with Jake”. He also
writes for a gaming blog with his brother-in-law called the “Ramblings of
Jacob and Delos”.
His brother writes a JavaScript blog called JoeZimJS and works with
his best friend on a gaming YouTube channel called “Bork & Zim Gaming,”
which Jacob helps out with on occasion.
Programming Ideas with Jake
http://programmingideaswithjake.wordpress.com/
Ramblings of Jacob and Delos
http://www.ramblingsofjacobanddelos.com/
JoeZimJS
http://www.joezimjs.com
vii
About the Technical Reviewer
Michael Thomas has worked in software development for more than 20
years as an individual contributor, team lead, program manager, and vice
president of engineering. Michael has more than 10 years of experience
working with mobile devices. His current focus is in the medical sector,
using mobile devices to accelerate information transfer between patients
and health care providers.
ix
Acknowledgments
In order to be sure that I got everything right—it would really suck for a
“comprehensive guide” to be missing a big chunk of functionality or to get
anything wrong—I enlisted the help of some Python experts on the first
edition. In return for their help, I let them introduce themselves to you
here. That’s not all I did in return, but it’s all you’re going to see :)
Emanuel Barry is a self-taught Python programmer who loves pushing
the language to its limits as well as exploring its darkest corners. He has to
do a lot of proofreading and editing for a local non-for-profit organization,
and decided to combine his love of Python and knowledge sharing with
his background in proofreading to help make this book even better. He can
often be found in the shadows of the mailing lists or the issue tracker, as
well as the Python IRC channel, as Vgr.
Chris Angelico has played around with Python since the late 90s, getting
more serious with the language in the mid 2000s. As a PEP Editor and active
participant in the various mailing lists, he keeps well up to date with what’s
new and upcoming in the language and also shares that knowledge with
fledgling students in the Thinkful tutoring/mentoring program. When not
coding in Python, he is often found wordsmithing for a Dungeons & Dragons
campaign, or exploring the linguistic delights of Alice in Wonderland and
similar works. If you find a subtle Alice reference in this text, blame him!
https://github.com/Rosuav
xi
Introduction
Python is a remarkable language with many surprisingly powerful features
baked into it. Generators, metaclasses, and decorators are some of those,
but this book is all about descriptors.
Code Samples
All code samples are written in Python 3, since that is the most recent
version, but all the ideas and principles taught in this book apply to Python
2 as well, as long as you’re using new style classes.
Note Superscript letters like the one at the end of the previous line
are in reference to the bibliography at the back of the book, which
includes URLs to the referenced site.
xiii
Introduction
xiv
PART I
About Descriptors
Part I is a deep explanation of what descriptors are, how they work, and
how they’re used. It gives enough information that you should be able to
look at any descriptor and understand how it works and why it works that
way, assuming the writer of the code made the code legible enough.
Creating your own descriptors isn’t difficult once you have the
information from Part I, but little to no guidance is given to help with
it. Instead, Part II covers that with a bunch of options for creating new
descriptors, as well as tips for avoiding common mistakes.
CHAPTER 1
What Is a Descriptor?
Put very simply, a descriptor is a class that can be used to call a method
with simple attribute access, but there’s obviously more to it than that. It’s
difficult to explain beyond that without digging a little into how descriptors
implemented. So, here’s a high-level view of the descriptor protocol.
A descriptor implements at least one of these three methods:
__get__(), __set__(), or __delete__(). Each of those methods has a list
of parameters needed, which will be discussed a little later, and each is
called by a different sort of access of the attribute the descriptor represents.
Doing simple a.x access will call the __get__() method of x; setting the
attribute using a.x = value will call the __set__() method of x; and using
del a.x will call, as expected, the __delete__() method of x.
4
Chapter 1 What Is a Descriptor?
Summary
As you have seen, descriptors occupy a large part of the Python language, as
they can replace attribute access with method calls, and even restrict which
types of attribute access is allowed. Now that you have a broad idea of how
descriptors are implemented as well as their use by the language, we will
dig a little deeper yet, gaining a better understanding of how they work.
5
CHAPTER 2
The Descriptor
Protocol
In order to get a better idea of what descriptors are good for, let’s finish
showing the full descriptor protocol. It’s time to see the full signatures of
the protocol’s methods and what the parameters are.
that it’s being called from the class level. But, if instance is not None, then it
tells the descriptor which instance it’s being called from. So an a.x call will
be effectively translated to type(a).__dict__['x'].__get__(a, type(a)).
Notice that it still receives the instance’s class. Notice also that the call still
starts with type(a), not just a, because descriptors are stored on classes.
In order to be able to apply per-instance as well as per-class functionality,
descriptors are given instance and owner (the class of the instance). How
this translation and application happens will be discussed later.
Remember—and this applies to __set__() and __delete__() as
well—self is an instance of the descriptor itself. It is not the instance that
the descriptor is being called from; the instance parameter is the instance
the descriptor is being called from. This may sound confusing at first, but
don’t worry if you don’t understand for now—everything will be explained
further.
The __get__() method is the only one that bothers to get the class
separately. That’s because it’s the only method on non-data descriptors,
which are generally made at a class level. The built-in decorator
classmethod is implemented using descriptors and the __get__()
method. In that case, it will use the owner parameter alone.
8
Chapter 2 The Descriptor Protocol
The last parameter is value, which is the value the attribute is being
assigned.
One thing to note: when setting an attribute that is currently a
descriptor from the class level, it will replace the descriptor with whatever
is being set. For example, A.x = someValue does not get translated to
anything; someValue replaces the descriptor object stored in x. To act on
the class, see the following note.
9
Chapter 2 The Descriptor Protocol
Summary
That’s the sum total of the descriptor protocol. Having a basic idea of how
it works, you’ll now get a high-level view of the types of things that can be
done with descriptors.
10
CHAPTER 3
E ncapsulation
One of the most useful aspects of descriptors is that they encapsulate
data so well. With descriptors, you can access an attribute the simple way
using attribute access notation (a.x) while having more complex actions
happen in the background. For example, a Circle class might have radius,
diameter, circumference, and area all available as if they were attributes,
but since they’re all linked, you only need to store one (we’ll use the radius
for the example) and calculate the others based on it. But from the outside,
they all look like attributes stored on the object.
Lazy Instantiation
You can use descriptors to define a really simple syntax for lazily
instantiating an attribute. There will be code provided for a nice lazy
attribute implementation later in the book.
In the Circle example, the non-radius attributes, after having their
caches invalidated, don’t need to calculate their values right away; they
could wait until they’re needed. That’s laziness.
Validation
Many descriptors are written simply to make sure that data being passed
in conforms to the class’ or attribute’s invariants. Such descriptors can
usually be designed as handy decorators, too.
Again with the Circle example: all of those attributes should be
positive, so all the descriptors could also make sure the value being set is
positive.
Triggering Actions
Descriptors can be used to trigger certain actions when the attribute is
accessed. For example, the observer pattern can be implemented in a
per-attribute sense to trigger calls to the observer whenever an attribute is
changed.
12
Chapter 3 What Are Descriptors Good For?
Last Circle example: all the “attributes” are based on the radius
calculated lazily. In order to keep from having to calculate them every
time, you could cache the result. Then, whenever one of them changes,
it could trigger invalidating all the others’ caches.
Encapsulation
Wait… encapsulation was a pro. How can it also be a con? The problem
is that you can hide incredible amounts of complexity behind something
that just looks like attribute use. With getters and setters, the user at least
sees that there’s a function being called, and plenty can happen in a single
function call. But the user won’t necessarily expect that what is seemingly
attribute access is causing something else to happen, too. Most of the time,
this isn’t a problem, but it can get in the user’s way of trying to debug any
problems, since clearly that code can’t be a problem.
13
Chapter 3 What Are Descriptors Good For?
Additional Objects
Because descriptors add another layer of indirection/abstraction to the
mix, they also add at least one additional object in memory, along with at
least one additional call stack level. In most cases, it’ll be more than one of
each. This adds bloat that could at least be partially mitigated using getters
and setters.
Summary
Descriptors are awesome, allowing for a variety of nice features that are
good at hiding their complexity from users of your code, but you should
definitely be aware that the power comes with cost.
14
CHAPTER 4
Descriptors in the
Standard Library
There are three basic, well-known descriptors that come with Python:
property, classmethod, and staticmethod. There’s also a fourth one that
you use all the time, but are less likely to know is a descriptor.
Of all the descriptors being shown in this chapter, it’s possible that
you only knew of property as a descriptor. Plenty of people even learn
the basics of descriptors from it, but a lot of people don’t know that
classmethod and staticmethod are descriptors. They feel like super
magical constructs built into the language that no one could reproduce in
pure Python. Once someone has an understanding of descriptors, though,
their basic implementation becomes relatively obvious. In fact, example
code will be provided for all three in simplified, pure Python code.
Lastly, it will be shown that all methods are actually implemented
with descriptors. Normal methods are actually done “magically,” since the
descriptor creation is implicit, but it’s still not entirely magical because it’s
done using a language construct the anyone could create.
What I find really interesting is that the first three are all function
decorators, which are another really awesome feature of Python that
deserves its own book, even though they’re way simpler.
class property:
def __init__(self, fget=None, fset=None, fdel=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
16
Chapter 4 Descriptors in the Standard Library
As you can now see, the property class has almost no real functionality
of its own; it simply delegates to the functions given to it. When a function
is not provided for a certain method to delegate to, property assumes that
it is a forbidden action and raises an AttributeError with an appropriate
message.
A nice thing about the property class is that it largely just accepts
methods. Even its constructor, which can be given all three methods at
once, is capable of being called with just one, or even none. Because of
this, the constructor and other methods can be used as decorators in a
very convenient syntax. Check out the documentation2 to learn more
about it.
Omitted from this code example is the doc functionality, where it sets
its own __doc__ property based on what is passed in through __init__()’s
doc parameter or using __doc__ from fget if nothing is given. Also omitted
is the code that sets other attributes on property, such as __name__, in
order to help it appear even more like a simple attribute. They did not
seem important enough to worry about, since the focus was more on the
main functionality.
17
Chapter 4 Descriptors in the Standard Library
class classmethod:
def __init__(self, func):
self.func = func
18
Chapter 4 Descriptors in the Standard Library
class staticmethod:
def __init__(self, func):
self.func = func
Regular Methods
Remember that it was stated earlier that regular methods implicitly use
descriptors as well. In fact, all functions can be used as methods. This is
because functions are non-data descriptors as well as callables.
Here is a Python implementation that roughly shows how a function
looks.
class function:
def __call__(self, *args, **kwargs):
# do something
19
Other documents randomly have
different content
Madge gasped. That the creature was mad, at the best, she made
no doubt. But that conviction, in the present situation, was of small
assistance. What was she to do?
"Look here!" Laying one hand against his arm, with the other she
pointed at the sitting-room window. His glance followed her finger-
tips.
"Who's that?"
"I don't quite follow you. Do you mean that you don't know who
she is?"
"I only know that I've been away all day, and that on my return I
find her there. How she got there I can't say--but she seems
determined to keep me out."
"You don't mean that! And have you no notion who the woman
is? She looks half mad."
"I should think she must be quite mad. It's the woman who
forced herself into the house the day before yesterday after you had
gone--that's all I know of her. This time she is not alone; she has a
man in there with her."
"If you wouldn't mind, I'd rather you opened the door."
"This house is mine--mine! It's all of it mine! And who are you,
that you ask such a question--of a lady?"
"And pray, sir, what explanation have you to offer of the felony
you are committing?"
"Of course!--ain't that what I'm saying? She come here, and she
took a key out of her pocket, and she put it in the keyhole, and she
opened the door, all quite regular, and she says, 'This here's my
house,' and she asked me to come in, so of course I come in."
"Do you mean to say that she gained entrance to this house by
means of a key which she took from her pocket?"
The woman put her hand up to her neck, for the first time
showing signs of discomposure.
"The key?"
Starting back, she looked about her wildly, and broke into a series
of shrill exclamations.
"The key!--my key!--no!--no!--no!--It is all I have left--the only
thing I've got. I've kept it through everything--I've never parted from
it once. I won't give it you--no!"
"By all means, if you wish it. Get rid of him at once. At the best
the fellow is an impudent intruder, and the story he tells is a
ridiculously lame one. He must have been perfectly well aware that a
woman of this sort was not likely to possess a house of her own,
and that accepting what he calls her invitation he was committing
felony."
The fellow in question shook his head as if he felt himself ill-used.
"I call that hard--cruel hard. If the young lady was to think of it
for half a moment she'd see as it was cruel hard."
"The young lady declines to think of it. Have the goodness to take
yourself away, and consider yourself lucky that you are allowed to
escape scot free."
"Let him go. There are one or two things about which we should
like to speak to you, this young lady and I, after he has gone."
But she would have none of him. Shrinking back, she stared at
him, in silence, for a second or two; then began to shriek at him like
some wild creature.
"I won't stay!--I won't!--I shall go!--I shall! You tried to get my
key--my key! You touch it--you dare! You asked me if my name"--
she stopped, stared about as if in terror, gave a great sigh, "You
asked me if my name----"
"There you are, you see--drove her out of her seven senses! So
you have."
For some reason, when Mr. Bruce Graham and Miss Brodie were
left alone, nothing was said about the recent visitors.
"If you'll sit down and wait," remarked Miss Brodie, "I'll go and
take my things off."
She brought in the wood, and the coal, and the paper; and then
she went to fetch the matches. When she returned she caught him
in the act.
"That's right! Men always use their fingers to put coal on the fire--
if they can. It's an agreeable habit."
He continued calm.
"Now you had better go and wash your hands. You'll have to do it
in the scullery; and by the time you're done, the fire will be out."
But the fire was not out. It was a complete success. The kettle
was put on, preparations were made for tea, and the table was laid,
Graham showing a talent for rendering assistance which was not
accorded the thanks it might have been. Madge was chilly.
"I should imagine you were rather a handy person to have about
the house."
"If you wish me to understand that you would rather not play,
have the goodness to say so plainly."
When he stopped, she was looking away from him, toward the
fire. Tears were in her eyes.
He played; and she sat and listened, in the firelight, till Ella came
home to tea.
It was while they were seated at table that Bruce Graham told
them of the result of his investigations. Although, for some reason,
the subject had not been mentioned when Madge and he had been
alone together, that young lady showed herself alert and eager
enough then. Nor, in that respect, was Ella behind her friend, while
Martyn concealed an interest which was probably equal to theirs
under ponderous attempts at jocularity.
"I have at least found that everything points to there being such a
hidden treasure--in spite of Jack's pretended scepticism."
Ella interposed.
The young lady looked the eagerness which the words suggested-
-like an imaginative child who pictures the materialisation of some
favourite tale of faerie.
"To begin with, I went to the house agents to learn for whom
they are acting."
"It is awfully good of you to take so much trouble. And what did
they say?"
"'Not insane?'
"'Insane?--No; he was as sane as you are--every whit. But he was
a disappointed man. He was malformed--the muscles of one leg
were paralysed. As he grew older, the paralysis increased, until it
extended up the whole of one side, and, at last, it killed him. He
married a girl who acted as book-keeper at an hotel, at which he
was in the habit of stopping, at Ilfracombe. She turned out a regular
bad lot--finally running away with a man named Ballingall.'
"'Charles Ballingall?'
"'Have you? Then let me inform you, without prejudice, that you
have acted for as rascally a scamp as ever trod the earth. Ossington
regarded him as a particular friend; and, as particular friends
sometimes have a knack of doing, he borrowed no end of money
from Ossington, ending by robbing him not only of his money, but of
his wife as well. The double blow almost broke Ossington's heart,
and during the remainder of his existence he lived the life of a
recluse. But, until then, we had acted for him continually. For
instance, we had acted for him in the purchase of Clover Cottage.'
"'Not a deed. We hold nothing. All that we have are the various
letters which he wrote to us at various times, on business. We had
heard nothing of him for months, when one morning we received a
telegram asking us to go at once to Clover Cottage. I went myself--I
liked the man. He was, in his way, as fine a gentleman as I ever
met. He had been cruelly used by friend and fortune. I found him
dead--alone in the house there, with a maid and a doctor; dead--
killed, according to the medical testimony, by a paralytic affection of
the heart; but actually, as sure as you and I are alive, by the wicked
wanton usage of those he had held dear. Now here the queer part of
the thing comes in.
"'His last words had been an instruction to send for us; but that
was the only instruction he had given. I myself searched the house
from top to bottom, and, as you know, it is not a large one. I had it
searched by others--every nook and cranny. Not a scrap of writing
could be discovered--letter, note, or memorandum. Not a document
of any sort of kind. Nothing whatever to show of what he had died
possessed, or to whom it was to go.'
"'It is. We furnished the Bank with a copy of the list, requesting
them to notify us should one of them come in: as yet not a single
one of them has made its appearance. Where are those notes?
Surely, if they were in the possession of any living person, ere this
some of them would have been presented. Where are the title deeds
of Clover Cottage--and of other properties, of which he was the
undoubted owner? He is the registered holder of ten thousand Great
Northern Railway Stock. Since his death, the dividends on it have
remained unclaimed. Where is the scrip? With the rest, has it
vanished into air? In a box in his bedroom were forty-seven pounds
in gold. That was all the cash the house contained. We buried him in
Wandsworth Cemetery; Hawkins, I, and the doctor were the only
mourners. We sold the furniture, paid the expenses, and the balance
stands to the credit of the estate. We advertised for next of kin,
without results. We advertised also for information as to the
whereabouts of any property of which he might have died
possessed--such as title-deeds, and anything of that kind. You
understand that there is a delicate question as to who is entitled to
collect the rents of other properties which we believe to have been
his freehold. But nothing came of that. Clover Cottage we placed in
the hands of Messrs. Parker and Beading, but only recently have
they succeeded in letting it--I believe to two single ladies.'
"'So I understand.'"
"You are the two single ladies. You," pointing to Ella, "are one of
them, and you," pointing to Madge, "are the other."
"'And you, Mr. Nicholls,' I said, 'have you formed no theory of your
own upon the subject?'
"Old Nicholls leaned back in his chair. He put his hands into his
two pockets, and he looked at me out of the corners of his eyes.
"'When found?'
"'He tilted his chair on to its hind-legs, watching me keenly all the
time.
"Madge," cried Ella, "did you hear that? That's exactly what you
said."
"'But where,' I said, 'do you think he is likely to have found such a
hiding-place?'
"'At Clover Cottage. I knew the man. The salient events of his life
happened there. In his whimsical way he regarded it as part and
parcel of himself. I have heard him say so half a dozen times. His
heart was in the place. Whatever he did conceal, was concealed
within its four walls. Before the furniture was sold, I had it
overhauled by an expert--some of the things were pulled to pieces.
His verdict was that nothing was hidden there. Had I had my way I
would have dismantled the whole house--only Hawkins was against
me. He said very properly, that if the heir-at-law proved
cantankerous, I might be made to smart in damages to the tune of a
pretty penny. So I abstained. All the same, if the house was in the
market to-morrow, I'd be a purchaser at a good round sum--if all
rights of treasure trove went with it. You may tell the present
tenants'--here he looked at me in a fashion which took me a little
aback--'if you have the honour of their acquaintance, that we keep a
sharp eye on the property; that it is not to be tampered with to the
extent of one jot or tittle; and that not so much as one inch of paper
is to be taken off the wall except with our express permission.'"
"Does it? It does nothing of the kind. Not after what I found in
this very room last night. In the face of that, I care nothing for Mr.
Nicholls, or for his threats either. What do you think yourself, Mr.
Graham?"
"If you will allow me, I will give you my own opinion when I have
told you of all that passed between Mr. Nicholls and myself. Indeed,
I am now coming to that very point."
"There you are, you see. You will not let the man finish, you really
won't. I never saw anything like you women for interrupting--never
in all my life."
"'I ought to, seeing that some one of the name of Edward John
Hurley is in our office at this moment, and has been in our office for
something over a quarter of a century.'
"I had noticed that Mr. Nicholls and he had exchanged glances
when I first put my query. Now he looked at his principal evidently in
search of guidance.
"'I do remember the 22nd of October, 1892, and the whole of the
circumstances. I chanced to meet Mr. Ossington in Holborn as I was
leaving the office. He asked me if I would dine with him in his house
at Wandsworth. I went with him to dinner there and then. After
dinner he asked me if I would witness his signature. I expressed my
willingness. I witnessed it.'
"'I was not. I have often wondered what it was, especially in the
light of after events. The document, which was on a sheet of blue
foolscap, had evidently been prepared before my arrival: Mr.
Ossington, covering the writing with a piece of blotting-paper, signed
it, in the middle of the page, directly underneath, while I affixed my
signature, as witness, on the left-hand side.'
"'Was there another witness?'
"'I never heard it. I only know that he called her Louisa. I think I
should recognise her if I saw her again. She was a red-faced, light-
haired, strapping wench, about eighteen years of age.'
"I thanked him for his frankness, and rose to go. Nicholls stopped
me.
"That is another question. The will gives neither you nor any one
else a title for the destruction of property. It simply conveys to the
finder the possession of certain things which are not specifically
mentioned. But it authorises no one to look for those things, still less
to do damage while looking."
"I don't say that. My advice is to put the legal aspect aside, and
to regard the common-sense one only. The will says that certain
things, when found, are to become the property of the finder, and
this house with them. You have reason to believe that those things
are concealed within this house. Then it is for you to consider
whether it is worth your while to run the risk of becoming
responsible for any damage you may do in case of your failure to
find those things. My opinion is, that it is worth your while to run
that risk--that it is worth any one's while to run that risk."
Madge stood up, with resolute lips, and sparkling eyes. She struck
her hand upon the table.
"The point is," said Ella, "where are you going to begin to look?"
"But, my dear, even if you set about the business in that drastic
fashion, you'll require method. How are you going to begin to take
the house to pieces--by taking the slates off the roof, and the
chimney-pots down?"
"And by taking the windows out of their frames, and the doors off
their hinges, and displaying the grates in the front garden! George!
Welcome to our website – the ideal destination for book lovers and
knowledge seekers. With a mission to inspire endlessly, we offer a
vast collection of books, ranging from classic literary works to
specialized publications, self-development books, and children's
literature. Each book is a new journey of discovery, expanding
knowledge and enriching the soul of the reade
Our website is not just a platform for buying books, but a bridge
connecting readers to the timeless values of culture and wisdom. With
an elegant, user-friendly interface and an intelligent search system,
we are committed to providing a quick and convenient shopping
experience. Additionally, our special promotions and home delivery
services ensure that you save time and fully enjoy the joy of reading.
textbookfull.com