OOP in Python
Before proceeding, I will explain some features of object-oriented programming (OOP) in a Python context.
The most basic element of any modern application is an object. To a programmer or architect, the world is a collection of objects. Objects consist of two types of members: attributes and methods. Members can be private, public, or protected. Classes are data types of objects. Every object is an instance of a class. A class can be inherited in child classes. Two classes can be associated using composition.
In a Python context, Python has no keywords for public, private, or protected, so encapsulation (hiding a member from the outside world) is not implicit in Python. Like C++, it supports multilevel and multiple inheritance. Like Java, it has an abstract keyword. Classes and methods both can be abstract.
The following code is an example of a generic web crawler that is implemented as an airline’s web crawler on the Skytrax site and as a retail crawler for the Mouthshut.com site. I’ll return to the topic of web crawling in Chapter 2.
from abc import ABCMeta, abstractmethod import BeautifulSoup import urllib import sys import bleach
#################### Root Class (Abstract) #################### class SkyThoughtCollector(object): __metaclass__ = ABCMeta
baseURLString = "base_url"
airlinesString = "air_lines"
limitString = "limits"
pageFile = urllib.urlopen(url) Chapter 1 IntroduCtIon
baseURl = ""
airlines = []
limit = 10
@abstractmethod
def collectThoughts(self):
print "Something Wrong!! You're calling an abstract method"
@classmethod
def getConfig(self, configpath):
#print "In get Config"
config = {}
conf = open(configpath) for line in conf: if ("#" not in line): words = line.strip().split('=') config[words[0].strip()] = words[1]. strip()
#print config
self.baseURl = config[self.baseURLString] if config.has_key(self.airlinesString): self.airlines = config[self. airlinesString].split(',') if config.has_key(self.limitString): self.limit = int(config[self.limitString])
#print self.airlines
def downloadURL(self, url):
#print "downloading url"
if pageFile.getcode() != 200: return "Problem in URL"
pageHtml = pageFile.read() pageFile.close()
return "".join(pageHtml)
def remove_junk(self, arg): f = open('junk.txt') for line in f: arg.replace(line.strip(),'')
return arg
def print_args(self, args): out =''
last = 0 for arg in args: if args.index(arg) == len(args) -1: last = 1 reload(sys)
sys.setdefaultencoding("utf-8")
arg = arg.decode('utf8','ignore'). encode('ascii','ignore').strip() arg = arg.replace('\n',' ') arg = arg.replace('\r','') arg = self.remove_junk(arg) if last == 0: out = out + arg + '\t' else: out = out + arg
print out
Chapter 1 IntroduCtIon
class AirLineReviewCollector(SkyThoughtCollector):
months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ]
def __init__(self, configpath):
#print "In Config"
super(AirLineReviewCollector,self).getConfig(configpath)
def parseSoupHeader(self, header):
#print "parsing header"
name = surname = year = month = date = country ='' txt = header.find("h9")
words = str(txt).strip().split(' ') for j in range(len(words)-1): if words[j] in self.months:
date = words[j-1]
month= words[j]
year = words[j+1]
name = words[j+3]
surname = words[j+4] if ")" in words[-1]:
country = words[-1].split(')')[0] if "(" in country:
country = country.split('(')[1] else:
country = words[-2].split('(')[1] + country
return (name, surname, year, month, date, country)
def parseSoupTable(self, table):
#print "parsing table"
images = table.findAll("img") over_all = str(images[0]).split("grn_bar_")[1].
split(".gif")[0]
money_value = str(images[1]).split("SCORE_")[1].
split(".gif")[0]
seat_comfort = str(images[2]).split("SCORE_")[1].
split(".gif")[0]
staff_service = str(images[3]).split("SCORE_")[1].
split(".gif")[0]
catering = str(images[4]).split("SCORE_")[1].
split(".gif")[0]
entertainment = str(images[4]).split("SCORE_")[1].
split(".gif")[0]
if 'YES' in str(images[6]):
recommend = 'YES' else:
recommend = 'NO'
status = table.findAll("p", {"class":"text25"}) stat = str(status[2]).split(">")[1].split("<")[0] return (stat, over_all, money_value, seat_comfort, staff_service, catering, entertainment, recomend)
def collectThoughts(self):
#print "Collecting Thoughts" for al in AirLineReviewCollector.airlines: count = 0 while count < AirLineReviewCollector.limit: count = count + 1 url = ''
if count == 1:
url = AirLineReviewCollector.
baseURl + al + ".htm" else:
url = AirLineReviewCollector.
baseURl + al + "_"+str(count)+ ".htm"
soup = BeautifulSoup.BeautifulSoup (super(AirLineReviewCollector,self). downloadURL(url))
blogs = soup.findAll("p", {"class":"text2"}) tables = soup.findAll("table", {"width":"192"}) review_headers = soup.findAll("td", {"class":"airport"}) for i in range(len(tables)-1): (name, surname, year, month, date, country) = self.parse
blog = str(blogs[i]). split(">")[1].split("<")[0] args = [al, name, surname, year, month, date, country, stat, over_all, money_value, seat_comfort, staff_service, catering, entertainment, recomend, blog] Chapter 1 IntroduCtIon
SoupHeader(review_headers[i]) (stat, over_all, money_value, seat_comfort, staff_service, catering, entertainment, recomend) = self.parseSoup
Table(tables[i])
Chapter 1 IntroduCtIon
super(AirLineReviewCo llector,self).print_ args(args)
######################## Retail Chield ########################
class RetailReviewCollector(SkyThoughtCollector):
def __init__(self, configpath):
#print "In Config"
super(RetailReviewCollector,self).getConfig(configpath)
def collectThoughts(self):
soup = BeautifulSoup.BeautifulSoup(super(RetailRev iewCollector,self).downloadURL(RetailReviewCollect or.baseURl))
lines = soup.findAll("a",{"style": "font-size:15px;"})
links = [] for line in lines:
if ("review" in str(line)) & ("target" in str(line)):
ln = str(line)
link = ln.split("href=")[-1].split ("target=")[0].replace("\"",""). strip()
links.append(link)
for link in links: soup = BeautifulSoup.BeautifulSoup( super(RetailReviewCollector,self). downloadURL(link))
comment = bleach.clean(str(soup.findAll("di v",{"itemprop":"description"})[0]),tags=[], strip=True)
tables = soup.findAll("table", {"class":"smallfont space0 pad2"})
parking = ambience = range = economy = product = 0 for table in tables: if "Parking:" in str(table): rows = table.findAll("tbody") [0].findAll("tr") for row in rows: if "Parking:" in str(row): parking = str(row). count("readbarfull")
if "Ambience" in str(row): ambience = str(row). count("readbarfull") if "Store" in str(row): range = str(row). count("readbarfull")
if "Value" in str(row): economy = str(row). count("readbarfull")
if "Product" in str(row): product = str(row).count ("smallratefull")
author = bleach.clean(soup.findAll("spa n",{"itemprop":"author"})[0], tags=[], strip=True)
date = soup.findAll("meta",{"itemprop":"dat ePublished"})[0]["content"]
args = [date, author,str(parking), str(ambience),str(range), str(economy), str(product), comment]
super(RetailReview Collector,self).print_ args(args)
if __name__ == "__main__":
if sys.argv[1] == 'airline': instance = AirLineReviewCollector(sys.argv[2]) instance.collectThoughts() else: if sys.argv[1] == 'retail': instance = RetailReviewCollector(sys.argv[2]) instance.collectThoughts() Chapter 1 IntroduCtIon
else:
print "Usage is"
print sys.argv[0], '<airline/retail>', "<Config File Path>"
The configuration for the previous code is shown here:
base_url = http://www.airlinequality.com/Forum/
#base_url = http://www.mouthshut.com/product-reviews/Mega-MartBangalore-reviews-925103466
#base_url = http://www.mouthshut.com/product-reviews/MegamartChennai-reviews-925104102
air_lines = emrts,brit_awys,ual,biman,flydubai limits = 10
I’ll now discuss the previous code in brief. It has a root class that is an abstract class. It contains essential attributes such as a base URL and a page limit; these are essential for all child classes. It also contains common logic in class method functions such as the download URL, print output, and read configuration. It also has an abstract method collectThoughts, which must be implemented in child classes. This abstract method is passing on a common behavior to every child class that all of them must collect thoughts from the Web. Implementations of this thought collection are child specific.
Calling Other Languages in Python
Now I will describe how to use other languages’ code in Python. There are two examples here; one is calling R code from Python. R code is required for some use cases. For example, if you want a ready-made function for the Holt-Winter method in a time series, it is difficult to do in Python. But it is Chapter 1 IntroduCtIon
Chapter 1 IntroduCtIon
available in R. So, you can call R code from Python using the rpy2 module, as shown here:
import rpy2.robjects as ro
ro.r('data(input)')
ro.r('x <-HoltWinters(input)')
Sometimes you need to call Java code from Python. For example, say you are working on a name entity recognition problem in the field of natural language processing (NLP); some text is given as input, and you have to recognize the names in the text. Python’s NLTK package does have a name entity recognition function, but its accuracy is not good. Stanford NLP is a better choice here, which is written in Java. You can solve this problem in two ways.
• You can call Java at the command line using Python code.
import subprocess subprocess.call(['java','-cp','*','edu. stanford.nlp.sentiment.SentimentPipeline', '-file','foo.txt'])
• You can expose Stanford NLP as a web service and call it as a service.
nlp = StanfordCoreNLP('http://127.0.0.1:9000')
output = nlp.annotate(sentence, properties={ "annotators": "tokenize,ssplit,parse,sentiment", "outputFormat": "json",
# Only split the sentence at End Of Line. We assume that this method only takes in one single sentence. "ssplit.eolonly": "true",
Chapter 1 IntroduCtIon
# Setting enforceRequirements to skip some annotators and make the process faster "enforceRequirements": "false" })
Exposing the Python Model as a Microservice
You can expose the Python model as a microservice in the same way as your Python model can be used by others to write their own code. The best way to do this is to expose your model as a web service. As an example, the following code exposes a deep learning model using Flask: from flask import Flask, request, g from flask_cors import CORS import tensorflow as tf from sqlalchemy import * from sqlalchemy.orm import sessionmaker import pygeoip from pymongo import MongoClient import json import datetime as dt import ipaddress import math
app = Flask(__name__) CORS(app)
@app.before_request def before():
db = create_engine('sqlite:///score.db')
metadata = MetaData(db)
Chapter 1 IntroduCtIon
g.scores = Table('scores', metadata, autoload=True)
Session = sessionmaker(bind=db)
g.session = Session()
client = MongoClient()
g.db = client.frequency
g.gi = pygeoip.GeoIP('GeoIP.dat')
sess = tf.Session()
new_saver = tf.train.import_meta_graph('model.obj.meta') new_saver.restore(sess, tf.train.latest_checkpoint('./')) all_vars = tf.get_collection('vars')
g.dropped_features = str(sess.run(all_vars[0]))
g.b = sess.run(all_vars[1])[0] return
def get_hour(timestamp): return dt.datetime.utcfromtimestamp(timestamp / 1e3).hour
def get_value(session, scores, feature_name, feature_value):
s = scores.select((scores.c.feature_name == feature_ name) & (scores.c.feature_value == feature_value))
rs = s.execute()
row = rs.fetchone()
if row is not None: return float(row['score']) else: return 0.0
@app.route('/predict', methods=['POST']) def predict():
input_json = request.get_json(force=True)
features = ['size','domain','client_time','device', 'ad_position','client_size', 'ip','root']
predicted = 0 feature_value = '' for f in features: if f not in g.dropped_features: if f == 'ip': feature_value = str(ipaddress. IPv4Address(ipaddress.ip_address (unicode(request.remote_addr)))) else:
feature_value = input_json.get(f) if f == 'ip': if 'geo' not in g.dropped_features: geo = g.gi.country_name_by_ addr(feature_value) predicted = predicted + get_ value(g.session, g.scores, 'geo', geo) if 'frequency' not in g.dropped_ features: res = g.db.frequency.find_ one({"ip" : feature_value}) freq = 1 if res is not None: freq = res['frequency'] predicted = predicted + get_ value(g.session, g.scores, 'frequency', str(freq)) Chapter 1 IntroduCtIon
if f == 'client_time':
feature_value = get_ hour(int(feature_value)) predicted = predicted + get_value(g. session, g.scores, f, feature_value)
return str(math.exp(predicted + g.b)-1)
app.run(debug = True, host ='0.0.0.0')
This code exposes a deep learning model as a Flask web service. A JavaScript client will send the request with web user parameters such as the IP address, ad size, ad position, and so on, and it will return the price of the ad as a response. The features are categorical. You will learn how to convert them into numerical scores in Chapter 3. These scores are stored in an in-memory database. The service fetches the score from the database, sums the result, and replies to the client. This score will be updated real time in each iteration of training of a deep learning model. It is using MongoDB to store the frequency of that IP address in that site. It is an important parameter because a user coming to a site for the first time is really searching for something, which is not true for a user where the frequency is greater than 5. The number of IP addresses is huge, so they are stored in a distributed MongoDB database.
High-Performance API and Concurrent Programming
Flask is a good choice when you are building a general solution that is also a graphical user interface (GUI). But if high performance is the most critical requirement of your application, then Falcon is the best choice. The following code is an example of the same model shown previously exposed by the Falcon framework. Another improvement I made in this code is that I implemented multithreading, so the code will be executed in parallel.
Another random document with no related content on Scribd:
Vihdoin saavuttiin koneenkäyttäjän portille. Miehemme otti itse häkin ja sydän tavallista enemmän pamppaillen kantoi sen sisälle suureen saliin. Rouva Gannett, yksinkertaisen näköinen, ruskeasilmäinen nainen, taputteli ihastuksesta käsiään.
"Eikö se ole kaunis?" kysyi mies ja katsoi vaimoonsa. "Ostin sen seuralaiseksesi yksinäisyydessäsi."
"Sinä olet liian hyvä minulle, Jem", sanoi vaimo ja tahtoen tarkastaa lintua selänpuoleltakin alkoi hän kiertää häkkiä. Mutta papukaija, sivumennen sanoen merkillisen epäluuloinen ja hermostunut olento, kiersi yhtä matkaa jättämättä rouvaa silmistään.
Kierrettyään viisi kertaa suuttui se vihdoin ja päästi kovin yksinkertaisen merimies-sukkeluuden.
"Ooh! — Jem!?!" huudahti rouva Gannett.
"Se puhuu kerrassaan mainiosti", sanoi Gannett hätäisesti, "ja niin viisas kun se on."
"Katso, sehän näyttää ymmärtävän mitä sinä sanoit!" huudahti rouva
Gannett. "Katso nyt miten se on viisas."
Tilaisuutta oli mahdoton jättää käyttämättä; muutamilla häikäilemättömillä valheilla kuvaili herra Gannett papukaijalle sovittamansa luonnonlahjat.
"Et suinkaan sinä sentään usko sitä?" kysyi rouva suu selkoselällään hämmästyksestä.
"Tietysti", vastasi koneenkäyttäjä varmasti.
"Mutta miten se voisi tietää toimeni ulkona ollessani?"
"Se on hänen salaisuutensa. Moni on kylläkin koittanut päästä sen perille, mutta kenellekään se ei ole onnistunut."
Rouva Gannett rypisti otsaansa ja katseli uteliaasti ihmeellistä elävää.
"Tulet vielä huomaamaan puheeni olevan totta", sanoi Gannett. "Matkalta palattuani saan kyllä kuulla mitä olet tehnyt."
"Ooh! — Herra minun luojani!" ihmetteli kummastunut rouva Gannett.
"Jos viivyt ulkona jälkeen seitsemän illalla, eli teet jotain muuta mikä ei minua miellyttäisi", jatkoi koneenkäyttäjä innoissaan, "kertoo se ne minulle. Vieraat, jotka käyvät luonasi, sanalla sanoen kaikki mitä teet ja toimit, saan kuulla."
"Minusta sillä ei ainakaan tule olemaan pahaa kerrottavana", sanoi rouva Gannett rauhallisesti. "Ellei se vaan valehtele."
"Sitä se ei taida", vakuutti mies. "Mene nyt ja hae hattusi ja kappasi, pistäydymme puoleksi tunniksi teatteriin."
Lausumansa sanat olivat profeetallisia, sillä kun teatterissa heidän vieressään istuva herrasmies hyväntahtoisesti tarjosi kiikariaan rouva Gannetille, nosti koneenkäyttäjä kauhean metelin — ja suunnilleen puolen tunnin kuluttua täytyi herrasväen johtokunnan erityisestä pyynnöstä jättää paikkansa.
"Aseta minut ensi kerralla mukaan ottaessasi hattukoteloon", huokasi rouva väsyneellä äänellä, koittaen pysyä raivoisasti
astelevan herransa ja miehensä rinnalla. "Mitä pahaa tuo herra teki?"
"Sinä jotenkuten mielistelit häntä", sanoi Gannett äkämystyneenä, "iskit silmää tai muuta sellaista. Ilman ei kukaan tule tarjoamaan kiikariaan naisille."
Rouva Gannett pudisti päätään niin tarkoittavasti, että eräs jo sivumennyt herrasmies kääntyi erityisesti katsomaan taakseen. Herra Gannett tarttui kiivaasti vaimonsa käsivarteen ja kiiruhti askeleitaan äärimmilleen suuttuneena.
Seuraavana aamuna oli hänen vihansa jo lauhtunut, mutta epäluulo kiusasi kahta tulisemmin. Syötyään aamiaista antoi hän tarkat neuvot miten vaimon oli käyttäydyttävä tullessaan iltapäivällä laivaan jäähyväisiä ottamaan ja lähti sitten Isoon Kuoviin.
Jäätyään yksin siivosi rouva Gannett huoneita, mutta päästyään lintuhäkille jäi hän katselemaan kaijaa. Hän oli huomaavinaan linnun silmissä pahanilkisen välähdyksen ja tapa millä se sulki ja aukaisi niitä, muistutti parhaiten viekasta silmäniskua.
Kun hän vielä seisoi ja katseli tätä merkillisyyttä, kolkutettiin ovelle ja sisään laahusti pieni virkeän näköinen nais-olento ja tervehti yliluonnollisella sydämellisyydellä.
"Tulin sinuakin tervehtimään ohikulkiessani", sanoi hän iloisesti, "ja ellei sinulla ole mitään vastaan lähden satamaankin jäähyväisiä ottamaan?"
Rouva Gannett, joka luuli miehensäkin olevan mielissään "esiliinasta" suostui kernaasti tarjoukseen.
"Kaunis lintu teillä", sanoi rouva Cluffins ja osotti päivänvarjollaan papukaijaa.
"Älä tee sitä!" sanoi rouva Gannett kiireesti.
"Miksikä ei?"
"Se pian sanoo jotain kauheaa."
"Puhuuko se? Minun täytyy välttämättömästi saada se sanomaan jotakin", huudahti rouva Cluffins innokkaasti.
Vieden tulipunaisen päivänvarjonsa aivan lähelle häkkiä, levitti hän sen yht'äkkiä selko selälleen. Papukaija hämmästyi jotta henki oli pysähtyä kurkkuun.
"Eihän se mitään puhu", sanoi rouva Cluffins.
Papukaija hyppäsi häkkinsä ulompaan nurkkaan ja sanoi jotakin matalalla äänellä. Huomatessaan, ettei mitään kauheampaa tapahtunutkaan, toisti se lauseensa vähän kovempaa ja tultuaan vakuutetuksi, ettei näkemänsä voinut mitenkään vahingoittaa, hyppäsi se takaisin entiselle paikalleen ja alkoi syytää mitä rivoimpia hävyttömyyksiä.
"Fyi, fyyii!" kirkui rouva Cluffins, punasempana kuin päivänvarjonsa.
"Olisipa se minun niin niskat paikalla siltä nujertaisin."
"Sen kyllä jättäisit tekemättä", sanoi rouva Gannett kunnioittavasti ja alkoi kertoa linnun merkillisiä ominaisuuksia.
"Mitä?" huudahti rouva Cluffins vaivalla pysyen tuolillaan. "Onko miehesi todellakin sanonut tuonlaista?"
"Hän on hirveän mustasukkainen", selitti toinen syvään huoaten.
"Toivoisin hänen olevan minun mieheni", sanoi rouva Cluffins päättävällä äänellä. "Toivoisinpa, että Cluffins puhuisi minulle jotain tuontapaista. — Toivoisinpa todellakin, että Cluffins puhuisi!"
"Se vaan todistaa hänen rakastavan minua", sanoi rouva Gannett katse lattiassa.
"Ja sinä uskot tuollaista moskaa?" sanoi rouva Cluffins ja nosti halveksivasti pientä nenän-nypykkäänsä.
"Enhän minä sitä usko", vastasi ystävätär, "mutta annan hänen kuitenkin luulla sen tekeväni."
"Se on hävytöntä!" sanoi rouva Cluffins ja heilutteli hurjasti päivänvarjoaan. "Tuon veroista en konsanaan ole edes kuullut. Pitäisipä minun päästä lukemaan lakia herra Gannetille — edes puoleksi tunniksi. Olen varma että hänestä tulisi toinen ihminen ennen pitkää."
Rouva Gannett koetti parhaansa mukaan rauhoittaa kiihottunutta ystävätärtänsä. Mutta kun kaikki keinot näyttivät turhilta haki hän päällysvaatteensa ja naiset läksivät ulos.
Saavuttuaan satamaan ja noustuaan Isoon Kuoviin, oli rouva Cluffins jo täydellisesti saavuttanut entisen levollisuutensa. Kierrellen ympäri höyrylaivan kantta teki hän kaikenlaisia kysymyksiä, enemmän saadakseen puhetta aikaan, kuin todellisesta uteliaisuudesta.
"Olen ajatteleva sinua joka päivä", sanoi hellästi rouva Gannett miehelleen.
"Ja minä sinua joka minuutti", vastasi tämä.
Hän huokasi ja vilkasi kiukkuisesti rouva Cluffinsia, joka naurussa suin liehakoitsi laivan nuorta alaperämiestä.
"Hän on merkillisen vilkas ja iloinen ihmisluonto", sanoi rouva Gannett, seuraten miehensä katsetta.
"Kyllä — se pitää myöntää", vastasi herra Gannett kuivasti, kun mitään pahaa aavistamaton rouva Cluffins leikillisesti näpsähytti perämiestä päivänvarjollaan. "Ihmeellinen ystävä hän ainakin Jenkins'in kanssa näyttää olevan. Ja panempa veikkaa, etteivät he koskaan ennen ole tavanneet toisiaan."
"Lapsi raukka", sanoi rouva Cluffins juhlallisesti lähestyen puhuvia. "Älkää ollenkaan olko huolissanne, herra Gannett, minä kyllä pidän seuraa teidän kyyhkysellenne."
"Kiitoksia", vastasi koneenkäyttäjä pitkäveteisesti.
"Meille tulee erinomaisen hauskaa", jatkoi rouva Cluffins. "Miten usein olenkaan toivonut olevani merimiehen vaimo. Oikein kadehdin heidän vapauttaan."
"Kadehditte mitä?" kysyi Gannett käheästi. "Heidän riippumattomuuttaan ja vapauttaan", selitti rouva Cluffins totisena. "Olen aina kadehtinut merimiesten vaimoja. He saavat tehdä mitä tahtovat! Yhdeksään, kymmeneen kuukauteen vuodesta ei heillä ole miestä, joka aina olisi kintereillä." Ennenkuin onneton koneenkäyttäjä ennätti keksiä sopivaa vastaväitettä, kuului käsky
komentosillalta ja hätäisen jäähyväisen jälkeen katosi hän konehuoneeseen. Kuulumattomat saivat poistua laivasta, portaat vedettiin sisään ja Iso Kuovi lähti hiljalleen liukumaan alas jokea.
Molemmat naiset seisoivat laiturilla kunnes laiva katosi joen mutkan taakse. Kaipaus sydämessään, joka ystävättären neuvon mukaan kyllä paranisi kupillisesta hyvää kahvia, saapui rouva Gannett yksinäiseen kotiinsa.
Leskenä oloaikana mikä nyt seurasi olivat rouva Cluffinsin vierailut ainoana vaihtelevaisuutena rouva Gannettin yksitoikkoisessa elämässä. Papukaijasta ei seuralaiseksi ollut, sen puheet kun jatkuvasti olivat aivan auttamattoman yksinkertaisia. Rouva Cluffins ehdotteli sen myömistä, mutta siihen ystävätär ei suostunut millään ehdolla, ei vaikka viereisessä talossa asuva tullipäällysmies, vanha poika, jo oli luvannut siitä aivan kuulumattomia summia.
"Ihmettelenpä mitä tuolla linnulla kerran tulee olemaan kerrottavaa miehellesi", sanoi rouva Cluffins eräänä päivänä istuessaan Gannettin salissa, noin kuukauden kuluttua Ison Kuovin lähdöstä.
"Ehkä mieheni jo on unohtanut koko tyhmän jutun", vastasi rouva Gannett punastuen. "Kirjeissään ei hän milloinkaan ole maininnut sanaakaan koko linnusta."
"Myö pois se", sanoi rouva Cluffins päättävällä äänellä. "Sinulla ei ole mitään huvia siitä, mutta Habsonia se äärettömästi miellyttää."
Rouva Gannett pudisti päätään.
"Mieheni kiskoisi seinät talosta jos sen tekisin", sanoi hän säpsähtäen.
"Tee sinä vaan kuten käsken, seinistä minä kyllä vastaan. Olen jo sanonut Habsonille, että hän saa sen viidellä punnalla."
"Se ei mitenkään käy laatuun", vastusti rouva Gannett levottomasti.
"Anna minun hoitaa koko asia", sanoi rouva Cluffins, katsellen mielihyvällä pieniä kätösiään. "Se tulee onnistumaan mainiosti, sen lupaan."
Hän kiersi käsivartensa ystävättären vyötäröille ja vei hänet ikkunan ääreen, alkaen puhua merkillisen vakavasti. Viiden minuutin kuluttua rouva Gannett vielä epäröi, kymmenen minuutin kuluttua oli hän jo antanut suostumuksensa ja viidentoista minuutin perästä oli aina toimelias rouva Cluffins jo matkalla Habsoniin, heiluttaen häkkiä, jotta papukaija sai pitää kiinni kynsin hampain pysyäkseen puolallaan. Rouva Gannett katsoi ikkunasta hänen jälkeensä ja istuutui sitten miettimään hyökkäys- ja puolustussuunnitelmaa tästä epäilemättä koituvassa ankarassa kamppailussa.
Noin viikko tämän tapahtuman jälkeen pysähtyi eräänä päivänä ajuri portin eteen ja koneenkäyttäjämme harppasi kolmella askeleella rappuset. Rynnättyään sisään sulki hän vaimonsa syliinsä voimalla, joka karhullekin olisi ollut kunniaksi. Syistä, joista voiman puute lihaksissa oli suurin, vastasi rouva Gannett syleilyyn vähemmän tulisena.
"Ooh! Miten ihanaa taas olla kotona!" huudahti Gannett, heittäytyi nojatuoliin ja veti vaimonsa polvelleen. "No, miten aikasi on kulunut? Ikävästi?"
"Tottuuhan sitä ihminen kaikkeen", vastasi rouva arasti.
"Niin, ja olihan sinulla papukaija."
"Niin, olihan minulla se."
"Mitenkä se nyt jaksaa?" kysyi koneenkäyttäjä, katsellen ympäri huonetta. "Missä se on?"
"Yksi osa on kaapin päällä", vastasi rouva Gannett, koittaen puhua rauhallisesti. "Toinen on hattukotelossa ja loppu tässä."
Hän pisti miehensä käteen halvan kaksiteräisen kynäveitsen.
"Kaapin päällä", toisti koneenkäyttäjä, hypistellen veistä sormissaan.
"Hattulaatikossa — -?!"
"Niin. Nuo siniset kukkajalustat."
Gannett tarttui otsaansa. Oliko hän kuullut oikein? Papukaija oli siis muuttunut kukkajalkapariksi, hatuksi ja veitseksi! Todellakin noiduttu lintu!
"Olen myynyt sen", lisäsi rouva Gannett kiireesti.
Herra Gannettin polvi muuttui yht'äkkiä ihmeellisen epäkohteliaaksi ja käsi putosi vyötäröiltä. Rouva nousi rauhallisena ja istuutui viereiselle tuolille.
"Myynyt?" toisti Gannett myrskyä ennustavalla äänellä. "Myynyt minun papukaijani?"
"En voinut kärsiä sitä, Jem. Ja kun en voinut kärsiä sitä, möin sen saadakseni nuo tarpeelliset esineet itselleni ja sinulle."
Koneenkäyttäjä lennätti veitsen toiselle puolelle huonetta.
"Katsos, Jem", jatkoi rouva Gannett, "se ei olisi kuitenkaan puhunut totta. Ja jos se olisi päässyt valehtelemaan, olisi se saanut aikaan kaikenlaisia ikävyyksiä."
"Se ei osannut valehdella", karjasi koneenkäyttäjä, juosten raivoisana edestakaisin huoneessa. "Paha omatuntosi saattoi sinut pelkäämään. Miten uskalsit sinä myydä minun papukaijani?"
"Sentähden, ettei se rakastanut totuutta, Jem", vastasi rouva hiukan vaaleana.
"Ainakin enemmän kuin sinä", ärjyi koneenkäyttäjä pysähtyen vaimonsa eteen. "Halpamainen olento!"
Rouva Gannett kaivoi nenäliinan taskustaan ja peitti silmänsä.
"Minä — minä möin sen sinun tähtesi Jem", änkytti hän. "Se valehteli niin kauheasti sinusta, enkä minä voinut kuunnella sellaisia."
"Minusta?" ihmetteli Gannett, vaipuen tuolille ja tirkistellen kummastuneena vaimoaan. "Valhetteli minusta? Tyhmyyksiä! Miten se olisi voinut olla mahdollista?"
"Yhtä suuri mahdollisuushan sillä oli puhua sinusta, kuin minustakin", sanoi rouva Gannett. "Se oli taitavampi noitumaan, kuin luulitkaan. Oo! Miten kauheita asioita se sinusta kertoi!"
"Luulet kai puhuvasi lapselle eli hullulle?" sanoi koneenkäyttäjä.
Rouva Gannett pudisti väsyneesti päätään.
"Olisipa todellakin hauska kuulla mitä historioita se tiesi — jos ehkä vielä satut muistamaan", sanoi Gannett purevan ivallisesti.
"Ensimäinen valhe", alkoi rouva heikolla, mutta selvällä äänellä "koski sinun Genuassa oloasi. Papukaija kertoi, että sinä kävit jossakin syrjäkaupungin varieteessa."
Kostea nenäliinan takaa tirkistävä silmä huomasi koneenkäyttäjän säpsähtävän.
"Minä en sellaista paikkaa luule löytyvänkään", kuului jatko.
"Kyllä — minä luulen — kyllä siellä sellaisia löytyy", änkytti mies.
"Se tahtoo sanoa — olen kuullut toverien puhuvan jotakin sinnepäin."
"Mutta sinä et siellä ole käynyt?" kysyi vaimo.
"En koskaan!" vastasi koneenkäyttäjä kiivaasti.
"Tuo hävytön lintu sanoi sinun olleen juovuksissa siellä", jatkoi rouva juhlallisella äänellä, "löit rikki pienen marmoripöydän ja kuritit kahta tarjoilijaa. Ja ellei 'Eteenpäin' kapteenia olisi ollut, olisit sinä joutunut putkaan ja jäänyt laivasta. Eikö se ollut halpamaista?"
"Inhoittavaa", vastasi mies matalalla äänellä.
"Luulen ettei 'Eteenpäin'-nimistä laivaa edes löydykään", jatkoi rouva Gannett.
"Ei se ainakaan laivan nimeltä kuulosta", mutisi Gannett.
"Niin, — muutaman päivän perästä saavuitte Neapeliin — — —."
"Siellä minä en kertaakaan käynyt kaupungissa", huomautti koneenkäyttäjä.
"Papukaija sanoi päinvastoin."
"Pitäisi sinun enemmän uskoa omaa miestäsi kuin tuollaista kirottua lintua", sanoi Gannett mielistellen.
"Tietystikin minä sen teen, Jem", vastasi vaimo, "tahdon vaan todistaa sinulle, että lintu valehteli koska et sitä muuten usko."
Gannett veti piipun taskustaan, kaivoi poron kopasta ja puhalsi päättävän näköisenä varteen.
"Eräs tyttö möi hedelmiä aivan laivan lähellä", jatkoi rouva. "Eräänä iltana ostit sinä viikunoita ja aioit samalla suudella häntä, mutta tytön sulhanen sattuikin näkemään ja aikoi iskeä sinua veitsellä. Sinä pelästyit, hyppäsit mereen ja olit vähällä hukkua."
Gannett viritti hitaasti ja huolellisesti piippunsa ja vei tulitikun pätkän uuniin.
"Minä tulin aivan sairaaksi sen jutuista — tuskin tiesin mitä tehdä", jatkoi rouva Gannett. "Ollessasi Suezissa — — —."
Koneenkäyttäjä teki kieltävän eleen. "Tarpeeksi", sanoi hän jäykästi.
"Niin, halusta en toistaisikaan mitä se kertoi Suezista", sanoi vaimo.
"Mutta ajattelin sen ehkä sinua huvittavan."
"Ei ollenkaan", vastasi koneenkäyttäjä ja puhalteli sankkoja sauhuja piipustaan. "Ei ollenkaan."
"Luultavasti nyt jo myöskin ymmärrät, miksi linnun hukkasin?" kysyi rouva Gannett. "Jos se tuonlaisia valheita olisi kertonut minusta sinulle, olisit sinä tietysti uskonut. Eikö totta?"
Gannett otti piipun suustaan ja kiersi uudestaan käsivartensa vaimonsa vyötäröille.
"En rakkaani", sanoi hän liikutettuna, "yhtävähän kuin sinäkään sitä tehnet."
"Ja minä tein oikein myödessäni sen? Eikö totta, Jem?"
"Aivan oikein", vastasi Gannett merkillisen sydämellisesti. "Se oli viisainta mitä taisit tehdä."
"Mutta pahinta et sinä vielä kuullut", sanoi rouva Gannett, "kun olitte
Suezissa — — —."
Gannettilta pääsi hiljainen sisällinen rukous Suezin edestä, hän löi nyrkkinsä pöytään ja kielsi vaimonsa koskaan mainitsemasta sitä nimeä. Sitten komensi hän illallista.
Vasta kuullessaan vaimonsa askaroivan kyökissä, muuttuivat hänen tuimat kasvonpiirteensä ja hän alkoi ajatella tätä asiaa puolelta toiselle, koittaen saada valoa tähän pimeään juttuun.
"Jenkins!" huudahti hän yht'äkkiä. "Jenkins ja rouva Cluffins. Ja minä kun aioin kertoa Cluffinssille hänen vaimonsa kirjeenvaihdosta. Luultavasti mies taitaakin ulkoa joka lapun."
ERÄS AAVEHISTORIA
Kaunis kesäpäivän aurinko paistaa helotteli täydellä terällään ja heikot tuulen hengähdykset kulettivat kuunaria tuskin kolmen solmun nopeudella.
Siellä täällä kiilsi joku valkea purje taivaanrannalla ja suuri kalalokki kierteli hitaasti leijaillen laivaa.
Kannella ei näkynyt elävää olentoa, joka olisi saattanut kuulla laivurin ja perämiehen välistä keskustelua tottelemattomuushengestä, jota viime aikoina oli huomattu miehistössä.
"Paljasta kiusaa se vaan on, on tottavieköön", sanoi laivuri, vanhanpuoleinen harmaapartainen mies.
"On niin", myönsi perämies.
"Ette usko mitä siihen aikaan syötiin, kun minä olin laivapoikana", jatkoi laivuri. "Ette usko vaikkapa vannoisin raamatun kautta."
"Ne ovat nyt vaativampia."
"Vaativampia!" huudahti laivuri. "Mikä oikeus nälkäisellä merimiehellä on olla vaativainen? Saavathan he tarpeekseen ruokaa. Mutta katsokaas nyt!"
Melkein tukahtuneena raivosta vetäytyi hän askeleen taaksepäin ja osoitti sormellaan Bill Smithiä, joka samassa nousi kannelle lautanen kädessään ja kasvot pahassa irvistyksessä. Tekeytyen aivan kuin ei huomaisikaan kapteenia meni hän laivan syrjälle ja kasvot toisaalle käännettynä heitti ruoan lautaselta mereen. Heti jälestä seurasi George Simpson yhtä häpeämättömällä tavalla tuhlaamaan laivurin syötäväksi ostamia ruokavaroja.
"Tuo tulee teille kalliiksi", mutisi laivuri hampaittensa raosta.
"Niitä on useampia", huomautti perämies.
Taas tuli kaksi miestä kannelle, irvistelivät kauheasti ja heittivät ruoan yli syrjän. Mutta nyt seurasi väliaika — väliaika, jolloin sekä perässä, että keulassa näyttiin odottavan jotakin, ja jolloin tuo "jotakin" parasta aikaa seisoi kanssin rappusilla ja koetti väkistenkin vääntää esille rohkeuttaan.
"Annappas pojan vaan tulla", kähisi laivuri, "niin nyljen hänet elävältä!"
"Ottakaa sitten vaan veitsi käsille", kehotti perämies.
Nenänpää valkeana ilmestyi poika kannelle vilkuillen rukoilevasti miehistöä takanaan. Mutta näiden ilkeä katse muistutti häntä jostakin, jonka oli unohtanut. Hän työnsi nyt laihan lautasta pitelevän kätensä mahdollisimman etäälle ja astui sydän kurkussa muutaman askeleen kohti syrjää.
"Poika!" karjasi laivuri yht'äkkiä.
"Niin, kapteeni?"
"Tule tänne!"
"Heitä ensin ruoka yli syrjän", kuului neljä matalaa uhkaavaa ääntä.
Poika pysähtyi epäröiden, vaan lähestyi sitten hitaasti laivuria.
"Mitä aiot tehdä päivällisellesi?" kysyi laivuri tuimasti.
"Syödä", vastasi nuorukainen peloissaan.
"Minkätähden sitä sitten kantelet ylös?" tiuskasi laivuri ja rypisti silmäkulmiaan.
"Ajattelin sen maistuvan paremmalta täällä."
"Maistuvan paremmalta", toisti laivuri pilkallisesti. "Eikö se ole mielestäsi kelvollista?"
"On."
"Puhu kovempaa", ärjäsi laivuri. "Onko se hyvää?"
"Mainiota", vastasi poika kimakalla kurkkuäänellä.
"Oletko koskaan saanut parempaa ruokaa?" kysyi laivuri ja korotti ääntänsä.
"En koskaan", kirkui poika, seuraten esimerkkiä.
"Kaikki on kuin pitääkin olla?" huusi laivuri.
"Paljon parempaa, sir", ärjäsi poika.
"Istu ja ala syödä", komensi laivuri.
Poika asettui varjoon ja ryhtyi päivälliseensä täysin tyytyväisen näköisenä.
"Otaksun", sanoi laivuri, hetken katseltuaan pojan syömistä, "otaksun, että toiset heittivätkin päivällisensä yli syrjän vaan siitä syystä, etteivät ole tottuneet niin hyvään ruokaan?"
"Niin minäkin luulen, sir", vastasi poika.
"Ovatko he puhuneet sellaista?" huusi laivuri.
Poika epäili ja vilkasi levottomana keulaan.
"Kyllä, sir", sanoi hän lopulta, mutta hätkähti samassa kuullessaan keulasta onnettomuutta ennustavaa murinaa.
Niin hitaasti kuin ruoka kuluikin, loppui se kuitenkin vihdoin ja poika vetäytyi keulaan katsellen rukoilevasti miehistöä.
"Tulehan tänne", sanoi Bill, "meillä on vähän puhumista kanssasi."
"Mahdotonta", vastasi poika. "Työt ennen kaikkia. En jouda juttelemaan."
Vasta illalla kun miehistön viha jo vähän oli lauhtunut, hiipi poika kojuunsa ja nukkui ennen pitkää vanhurskaan unta. Mutta muutaman tunnin kuluttua oli hän kuulevinaan miesten ääniä; hän raotti silmiään ja huomasi kummakseen miesten istuvan pöydän ääressä ja puhelevan.
"Kelpo selkäsaunan hän on ansainnut, totta totisesti", sanoi Bill ja pureskeli kuivaa leivänkappaletta.
"Ja saada kuusi kuukautta", sanoi vanha Ned. "Ei Bill, ei se kelpaa."
"No pitääkö meidän sitten viikkomääriä elää homehtuneella leivällä ja pahentuneilla perunoilla?" kysyi toinen raivoisasti. "Sehän on pitkäveteistä itsensä tappamista!"
"Tekisipä teistä joku edes itsemurhan", sanoi vanha Ned ja katsoi odottavan näköisenä tovereitaan. "Luulenpa, että se vähän pelottaisi ukkoa."
"Niin. Esimerkiksi juuri sinä. Olethan vanhin joukosta", sanoi Bill pistävästi.
"Billillä on oikein", lisäsi Simpson. "Sinun ikäiselläsi miehellä, Ned, ei enää voi olla suuria iloja tästä matoisesta mailmasta. Vai kuinka?"
"Niin ja sinä kirjoitat kirjeen laivurille; selität tekosi syyt: huono kohtelu ja kehno ruoka", sanoi kokki asiaan äärettömästi innostuneena.
"Älkää tyhmyyksiä jaaritelko", sanoi vanha mies ärtyisesti.
"Kuulkaapas pojat", sanoi yht'äkkiä Bill. "Minäpä tiedän mitä teemme. Joku meistä on tekevinään itsemurhan, kirjoittaa ukolle kirjeen kuten kokki sanoi ja selittää ennen hypänneensä mereen, kuin nälkään kuolevansa. Olen varma ukon pelästyksestä!"
"Ja lastiruumassa on hänelle mainio piilopaikka!" huomautti joku.
"Se ei taida ollakaan hullumpi keksintö", sanoi Ned pitkäveteisesti, "mutta kuka sinne menee?"
"Tommy!"
"Pakana vieköön, häntä en tullut ajatelleeksikaan", huudahti Ned ihastuneena. "Tulitko sinä, kokki?"
"En totisesti tullut minäkään."
"Katsokaas, on parasta, että Tommy sen tekee", sanoi Bill, "jo siitäkin syystä, että jos kepposemme keksitään pääsee poika koko jutusta paljaalla selkäsaunalla. Kuka luukut hänen jälkeensä on sulkenut, siitä emme tietystikään tiedä tuon taivaallista. Me kaipaamme vaan Tommyä ja löydämme vihdoin hänen kirjeensä."
Kokki nosti toisen jalkansa polvelleen ja nyökkäsi myöntävästi. Mutta samassa näytti hän huomanneen jotakin, irvisti ja osotti sormellaan kojua, josta Tommyn kasvot vaaleina ja kauhistuneina tirkistelivät miehiä.
"Halloo!" sanoi Bill. "Oletko kuullut puheemme?"
"Kuulin, että puhuitte vanhan Nedin tappamisesta", vastasi poika varovasti.
"Hän on kuullut kaikki", sanoi kokki päättävästi. "Tiedätkö minne valehtelevat pikkupojat joutuvat?"
"Ennen sinnekin menen kuin lastiruumaan", sanoi Tommy. "Minä en sinne mene. — Kerron laivurille kaikki."
"Sen jätät tekemättä", sanoi Bill lujasti. "Kas niin, ulos kojusta ja kiireesti, ellet apua tahdo!"
Ulvahtaen vetäytyi poika peitteensä sisään ja iski suonenvedontapaisesti sormensa kojun laitoihin. Siitä ei kuitenkaan ollut apua; jalkojaan sätkyttelevä poika nostettiin lattialle ja pantiin istumaan pöydän ääreen.
"Mustetta, kynä ja paperia, Ned", komensi Bill.
Ukko toi pyydettyä ja kun Bill ensin oli paitansa hihalla pyyhkinyt voipalasen paperilta, asetti hän kirjotusvehkeet uhrin eteen.
"En minä osaa kirjoittaa", sanoi Tommy epäröiden.
Miehet katsahtivat toisiinsa silminnähtävällä levottomuudella.
"Valehtelet", sanoi vihdoin kokki.
"En taida, sanon minä", intti poika. "Sentähden minut merellekin lähetettiin kun en lukemaan enkä kirjoittamaan oppinut."
"Tartuppa sen tukkaan vähäisen, Bill", sanoi Ned suuttuneena tällaisesta merimiehen ammatin häpäisemisestä.
"Antaa olla", vastasi Bill rauhallisesti. "Minä kirjoitan hänen edestään; ukko ei minun käsialaani tunne."
Istuutuen pöydän ääreen asetteli hän hetkisen itseään, pisti sitten kynän mustepulloon ja jäi hyvin ajattelevan näköisenä tirkistelemään rasvaista paperia edessään.
"Viisainta on kirjoittaa hyvin huonosti", esitti Ned.