python.blogowo.net

#!/usr/bin/env python

Jeśli życzysz sobie dodać tutaj swój blog, lub go usunć z tej planety - napisz maila na adres python(malpka)blogowo.net, zamieniając (malpka) na znak @. W przydaku prośby o dodanie załącz koniecznie adres rss/rdf/atom dla kategorii/taga python (jeśli oczywiście blog nie jest jedynie o Pythonie :-))

July 02, 2009

Riklaunim

Problem z ofertami pracy w sieci

Bazując m.in. na swoich doświadczeniach - jest masa serwisów oferujących oferty pracy. Jedne bardziej renomowane, inne "darmowe" i zawierające wiele słabych ofert. Są też "franczyzy" - oferty pracy z jednego serwisu udostępniane w drugim, czy też dodawanie tej samej oferty przez firmę na wielu stronach. Nie ma też chyba konkretnego użytecznego serwisu (poza Google) z prawdziwego zdarzenia będącego wyszukiwarką ofert z większości/wszystkich liczących się serwisów ofertowych. Generalnie zamieszczanie ofert jest płatne, więc jeżeli szukamy dobrych ofert - musimy przejrzeć wszystkie fajne serwisy żeby mieć pewność że sprawdziliśmy wszystkie fajne oferty.

Od strony Django jakby taki serwis, zbieracz ofert pracy mógł wyglądać? Najprostsze rozwiązanie to pełnotekstowa wyszukiwarka indeksująca oferty pracy. Konkurencja dla Google, lecz jeżeli zaoferuje trafniejsze wyszukiwanie (np. z filtrowaniem po branży, województwie czy mieście) to będzie poręczniejszy. Xapian, Whoosh, czy może na całego i Solr? Druga opcja to pobieranie (indeksowanie) ofert pracy, wyciąganie ich treści, dodatkowych danych i zapis we własnej bazie danych. W efekcie otrzymamy serwis podobny do innych ofertowych serwisów, lecz oferujący przegląd ofert z wielu źródeł. Kwestia to napisanie parserów dla każdego serwisu - listę najnowszych ofert zazwyczaj otrzymamy z RSS, do tego trochę regexów do wyciągnięcia treści ogłoszenia z gotowej strony, jak i dla każdego źródła będzie pewnie konieczne trochę poprawek różnego typu (CSS, w parserze), żeby treść importowała się w miarę poprawnie - chyba że i treść ogłoszenia rozbijemy na poszczególne składowe pozbywając się formatowania HTML źródła.

Dla przykładu dość prosty parser:

# -*- coding: utf-8 -*-
from datetime import datetime
import sys
from os import environ
import urllib2
from re import findall

environ['DJANGO_SETTINGS_MODULE'] = 'settings'

import feedparser

from settings import *
from django.contrib.sessions.models import *
# proof-of-concept app
from diamandas.jobmaster.models import *

############
## Jobexpress
############
source = JobSource.objects.get(title='Jobexpress')
## IT

data = feedparser.parse('http://www.jobexpress.pl/recent,it,84.html')
if data and 'entries' in data:
	for elem in data['entries']:
		sum = elem['summary_detail']
		published = sum['value'].split(',')[0].strip().split('-')
		published = datetime(int(published[2]), int(published[1]), int(published[0]))
		company = sum['value'].split(',')[1].strip()
		
		opener = urllib2.build_opener()
		opener.addheaders = [('user-agent', 'Opera/9.64 (X11; Linux x86_64; U; en) Presto/2.1.1'), ('referer', 'http://www.jobexpress.pl/recent,it,84.html')]
		o = opener.open(elem['link'])
		of = o.read()
		text = findall(r'(?xs)<ul\s*class="oferta_pracy">(.*?)<!--\s*eof:\s*oferta''', str(of))
		if text and len(text) > 0:
			text = text[0]
			# cleaning
			text = '<ul>' + text
			text = text.replace('href="', 'href="http://www.jobexpress.pl/')
			text = text.replace('src="', 'src="http://www.jobexpress.pl/')
			
			city = findall(r'(?xs)<span\s*class="cv_label">Miejsce\s*pracy:\s*</span>(.*?)</li>''', text)
			try:
				city = city[0].strip()
			except:
				city = '???'
			
			# TODO
			region = 'Na bazie miasta'
			
			branch = 'Informatyka/Programowanie'
			try:
				j = JobOffer.objects.get(position=elem['title'], company=company, city=city)
			except:
				j = JobOffer(position=elem['title'], company=company, source=source, url=elem['link'], indexed_at=datetime.now(), updated_at=datetime.now(),
						published_on_source=published, city=city, region=region, branch=branch, offer=text)
				j.save()
			
				print u'ZAPISANO: %s' % elem['title']
			else:
				j.updated_at = datetime.now()
				j.offer=text
				j.save()
				print u'ZAKTUALIZOWANO: %s' % elem['title']
			
A modele mogą wyglądać np. tak:
class JobSource(models.Model):
	"""
	Keeps a "list" of sites with job offers that are parsed by the application.
	Just for ease of data manipulation
	"""
	title = models.CharField(verbose_name='Tytuł', max_length=255)
	url = models.CharField(verbose_name='URL Strony głównej', max_length=255)
	css = models.TextField(verbose_name='Style CSS', blank=True)
	class Meta:
		verbose_name = 'Serwis z ofertami'
		verbose_name_plural = 'Serwisy z ofertami'
	def __str__(self):
		return self.title
	def __unicode__(self):
		return self.title

class JobOffer(models.Model):
	"""
	Indexed job offers from various sites
	"""
	position = models.CharField(verbose_name='Stanowisko', max_length=255)
	company = models.CharField(verbose_name='Firma', max_length=255)
	source = models.ForeignKey(JobSource, verbose_name='Źródło')
	url = models.CharField(verbose_name='URL oferty', max_length=255)
	indexed_at = models.DateTimeField(verbose_name='Data zaindeksowania oferty')
	updated_at = models.DateTimeField(blank=True, null=True, verbose_name='Data ostatniej aktualizacji')
	published_on_source = models.DateTimeField(blank=True, null=True, verbose_name='Data publikacji na stronie-źródle')
	is_inactive = models.BooleanField(blank=True, default=False, verbose_name='Oferta nieaktywna')
	city = models.CharField(verbose_name='Miasto', max_length=255)
	region = models.CharField(verbose_name='Województwo', max_length=255)
	branch = models.CharField(verbose_name='Branża', max_length=255)
	offer = models.TextField(verbose_name='Treść oferty')
	class Meta:
		verbose_name = 'Oferta'
		verbose_name_plural = 'Oferty'
	def __str__(self):
		return self.position
	def __unicode__(self):
		return self.position
Powyższy przykład jest dość prosty i sporo można rozbudować ten pomysł (jak i wyciągać rozbite dane, a nie zbitą treść oferty). Ale w efekcie można wyświetlić ogłoszenie u siebie (choć aplikowanie zazwyczaj wymaga przejścia na źródłowy serwis):
jobmaster.png
Normalny serwis z dobrą marką zarabia na publikacji ogłoszeń, a tego typu serwis mógłby zarabiać np. poprzez dodatkowe usługi sprzedawane indeksowanym serwisom (np. własny szablon, wyróżnianie ofert), jak i też standardowo na reklamach kontekstowych. Ważne żeby indeks był kompletny i łatwo dostępny. Czy jest miejsce dla dobrze napisanego serwisu tego typu? Myślę że tak... jakieś komentarze? :)

.c { color: #408080; font-style: italic } /* Comment */ .err { border: 1px solid #FF0000 } /* Error */ .k { color: #008000; font-weight: bold } /* Keyword */ .o { color: #666666 } /* Operator */ .cm { color: #408080; font-style: italic } /* Comment.Multiline */ .cp { color: #BC7A00 } /* Comment.Preproc */ .c1 { color: #408080; font-style: italic } /* Comment.Single */ .cs { color: #408080; font-style: italic } /* Comment.Special */ .gd { color: #A00000 } /* Generic.Deleted */ .ge { font-style: italic } /* Generic.Emph */ .gr { color: #FF0000 } /* Generic.Error */ .gh { color: #000080; font-weight: bold } /* Generic.Heading */ .gi { color: #00A000 } /* Generic.Inserted */ .go { color: #808080 } /* Generic.Output */ .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ .gs { font-weight: bold } /* Generic.Strong */ .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ .gt { color: #0040D0 } /* Generic.Traceback */ .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ .kp { color: #008000 } /* Keyword.Pseudo */ .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ .kt { color: #B00040 } /* Keyword.Type */ .m { color: #666666 } /* Literal.Number */ .s { color: #BA2121 } /* Literal.String */ .na { color: #7D9029 } /* Name.Attribute */ .nb { color: #008000 } /* Name.Builtin */ .nc { color: #0000FF; font-weight: bold } /* Name.Class */ .no { color: #880000 } /* Name.Constant */ .nd { color: #AA22FF } /* Name.Decorator */ .ni { color: #999999; font-weight: bold } /* Name.Entity */ .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ .nf { color: #0000FF } /* Name.Function */ .nl { color: #A0A000 } /* Name.Label */ .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ .nt { color: #008000; font-weight: bold } /* Name.Tag */ .nv { color: #19177C } /* Name.Variable */ .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ .w { color: #bbbbbb } /* Text.Whitespace */ .mf { color: #666666 } /* Literal.Number.Float */ .mh { color: #666666 } /* Literal.Number.Hex */ .mi { color: #666666 } /* Literal.Number.Integer */ .mo { color: #666666 } /* Literal.Number.Oct */ .sb { color: #BA2121 } /* Literal.String.Backtick */ .sc { color: #BA2121 } /* Literal.String.Char */ .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ .s2 { color: #BA2121 } /* Literal.String.Double */ .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ .sh { color: #BA2121 } /* Literal.String.Heredoc */ .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ .sx { color: #008000 } /* Literal.String.Other */ .sr { color: #BB6688 } /* Literal.String.Regex */ .s1 { color: #BA2121 } /* Literal.String.Single */ .ss { color: #19177C } /* Literal.String.Symbol */ .bp { color: #008000 } /* Name.Builtin.Pseudo */ .vc { color: #19177C } /* Name.Variable.Class */ .vg { color: #19177C } /* Name.Variable.Global */ .vi { color: #19177C } /* Name.Variable.Instance */ .il { color: #666666 } /* Literal.Number.Integer.Long */

July 02, 2009 02:08 PM

July 01, 2009

Jarosław Zgoda

Zapraszam na PyconPL 2009

Jeżeli ktoś jeszcze się zastanawia, czy wybrać się na PyconPL do Ustronia w październiku, to może przekonam go tym, że będę tam trzymał spicz na temat przygód Adama Słodowego w krainie ramówek webowych — rozłożę na czynniki pierwsze jakąś ramówkę (pewnie będzie to najbliższe mi Django) i spróbuję złożyć coś podobnego ...

July 01, 2009 10:52 AM

June 30, 2009

Riklaunim

Profilowanie kodu Pythona za pomocą Pympler

Pympler to zestaw narzędzi dla Pythona pozwalający mierzyć, monitorować i analizować wykorzystanie pamięci przez kod Pythona. Narzędzia te są dość dobrze udokumentowane i działają na wszystkich bieżących wydaniach Pythona.

June 30, 2009 12:12 PM

PyBonjour

Pythonowe API na usługę Bonjour do wykrywania urządzeń w sieci i usług jakie oferują.

June 30, 2009 11:25 AM

June 29, 2009

Riklaunim

Treść w Plone3

Przegląd dostępnych w Plone typów treści.

June 29, 2009 04:34 PM

Jarosław Zabiełło

Atak na zabezpieczenia protected/private Javy/Scali i PHP5

Być może jednym z argumentów, dla których Ruby znajduje więcej zainteresowania ze strony programistów Javy niż Python, jest podobny mechanizm zabezpieczenia dostępu do atrybutów klasy za pomocą słów kluczowych private i protected. W języku PHP 5 wprowadzono podobny mechanizm. Ci jednak, którzy myślą, że te kwalifikatory zakresu dostępu stanowią jakiekolwiek poważne zabezpieczenie, są w głębokim błędzie. W banalny sposób można to obejść zarówno w w PHP 5, jak i w Javie. Przy czym o ile, w Javie wynika to raczej z celowego projektu w mechanizmie refleksji, w PHP 5 wygląda to na dziurę bezpieczeństwa…

Aby przełamać zabezpieczenia dostępu do atrybutów private i protected w wypadku Javy wystarczy użyć refleksji. W związku z tym, że Scala korzysta z tego samego mechanizmu refleksji co Java, problem ten dotyczy tak samo Scali. Poniższy kod wyświetla wartości atrybutów private i protected.

   
import java.lang.reflect.{Field}

class SecureData  {
  private val priv = "my-private-value" 
  protected val prot = "my-protected-value" 
}

object Main {
  def main(args:Array[String]) {
    val obj = new SecureData()
    for(f <- obj.getClass.getDeclaredFields) {
      f.setAccessible(true)
      println(f.getName+"="+f.get(obj))
    }
  }
}

Powyższy kod nie jest jakimś hackerskim zabiegiem. Jest zwykłym użyciem tego co daje refleksja. Tzn. domyślnie metoda setAccessible pozwala zmienić dostęp do tych atrybutów (można to samo zrobić z metodami), o ile nie podejmiemy specjalnych działań zapobiegawczych poprzez użycie np. SecurityManagera.

W PHP 5 użycie refleksji nic nie pomoże, bo nie ma tam metody setAccessible. Jednakże, na skutek błędów w implementacji PHP 5 (sprawdzane włącznie z najnowszym, wydanym 2 dni temu, PHP 5.3 RC4) złamanie zabezpieczeń tego języka jest też bardzo łatwe. Poniższa funkcja expose wyświetli każdy atrybut danej instancji klasy, bez względu na to czy atrybut ten jest chroniony kwalifikatorem protected czy private.

<?php
class SecureData {
  private $priv="my-private-value";
  protected $prot="my-protected-value";
}

function expose($obj, $attr_name) {
  $a = (array)$obj;
  $r = new ReflectionProperty($obj, $attr_name);
  if ($r->isPrivate())
    return $a["\0".get_class($obj)."\0".$attr_name];
  elseif ($r->isProtected()) 
    return $a["\0*\0".$attr_name];
  else 
    return $obj->$attr_name;
}

$c = new SecureData;
echo expose($c, 'priv'); # => my-private-value
echo expose($c, 'prot'); # => my-protected-value
?>

Można też iść “na całość” i zmodyfikować treści udostępnianie przez te atrybuty, co może być trochę katastrofalne w skutkach. Wystarczy zamiast rzutowania do array’a użyć metody settype. Jedyną wadą jest to, że po użyciu settype zniszczona została cała informacja o klasie i interfejsie obiektu.

<?php
function hack(&$obj, $attr_name, $value) {
  $r = new ReflectionProperty($obj, $attr_name);
  $class = get_class($obj);
  settype($obj, 'array');
  if ($r->isPrivate())
    $obj["\0$class\0".$attr_name] = $value;
  elseif ($r->isProtected()) 
    $obj["\0*\0".$attr_name] = $value;
} 

$c = new SecureData;
hack($c, 'priv', 'my-hacked-private-value'); 
var_dump($c);
/*
array(2) {
  ["Cpriv"]=>
  string(23) "my-hacked-private-value" 
  ["*prot"]=>
  string(18) "my-protected-value" 
} 
*/
?>

Na tym tle stanowisko Pythona nie brzmi wcale aż tak źle. W Pythonie wszystkie metody i atrybuty są publiczne. Jeśli programista chce to ma dostęp do wszystkiego. I tak przecież będzie miał taki dostęp jak się uprze, więc po co mu utrudniać życie, prawda? Wg filozofii Pythona, jedyną zaletą posiadani takich kwalifikatorów dostępu byłoby to, aby uniknąć pomyłki a nie, aby bić programistę po łapach. W Pythonie rozwiązano to za pomocą tzw. “manglingu” nazw. Otóż metody (i atrybuty) których nazwy zaczynają się od dwóch znaków podkreślenia (ale równocześnie nie mogą się kończyć takimi znakami) są traktowane specjalnie przy próbie uzyskania do nich dostępu. Tzn. formalnie nadal są public (tak jak wszystko inne), trzeba jednak użyć trochę innej, lekko zamotanej, składni. I to jest wystarczające, aby programista się nie pomylił.

                   
class SecureData(object):
  __priv = "my-private=value" 
  _prot = "my-protected-value" 

try: 
  print SecureData.__priv
except AttributeError:
  print "Wrong syntax, you lame!" 

print SecureData._SecureData__priv 
# => my-private=value  

Zobacz też

by Jarosław Zabiełło at June 29, 2009 07:45 AM

June 28, 2009

Polish Python Coders Group

Python 3.1

Benjamin Peterson (Release Manager) ogłosił wydanie wersji 3.1 z nowej gałęzi Pythona 3.x. Wydanie to przynosi wiele zmian zarówno w bibliotece standardowej jak i w samym języku. Poniżej przedstawiamy najważniejsze zmiany: Nowe moduły: - collections.OrderedDict - collections.Counter - tkinter.ttk - importlib - moduł itertools wzbogacił się o dwie nowe funkcje: combinations_with_replacement() i itertools.compress() Warto wymienić jeszcze 2 istotne zmiany: W funkcjach format() i str.format(), można użyć separatora do oddzielenia grup trzycyfrowych(Thousands Separator). Składnia konstrukcji with pozwala teraz na stworzenie kilku Contex Manager-ów w jednej instrukcji. Python 3.1 to nie tylko nowości, ale również optymalizacje. Przy wydaniu wersji 3.0 pojawiło się wiele narzekań na bardzo słabą wydajność modułu io z tego powodu został on przepisany w języku C i jest on teraz od 2 do 20 razy szybszy. Podczas konfiguracji można użyć flagi --with-computed-gotos dzięki której można zanotować przyśpieszenie nawet o 20% (issue4753) Szczegółowy opis wszystkich zmian standardowo znajdziemy w dokumencie What's new w dokumentacji Pythona 3.1. Wydanie to nie obyło się jednak bez zgrzytów. Przed wydaniem wersji rc1 do biblioteki standardowej dodano bibliotekę do manipulacji adresami IP autorstwa kilku pracowników Google o nazwie ipaddr. W tym momencie rozgorzała ostra dyskusja na temat użyteczności tej biblioteki i sposobu jej działania. W trakcie dyskusji pojawiły się propozycje zastosowania innych bibliotek, które miałyby lepiej sprawować swoją funkcję. W końcu, wbrew polityce wydań wersji Release Candidate, przed wydaniem rc2 biblioteka ipaddr została usunięta. Cała sprawa była na tyle głośna, że doczekała się artykułu w magazynie lwn.net. Źródło: python.org

by Łukasz Oleś at June 28, 2009 03:57 PM

Riklaunim

Instalacja nowych skórek w Plone

Prosty przewodnik opisujący instalację nowych skórek za pomocą buildout

June 28, 2009 10:16 AM

Wprowadzenie do Plone

Podstawowe informacje, oraz lokalna instalacja Plone/Zope

June 28, 2009 08:44 AM

June 25, 2009

Riklaunim

Wyspa Mrozu, gra cRPG w Django - zrzut kodu dostępny

Postanowiłem wydać paczkę z obecnym kodem bardzo-rozwojowym Wyspy Mrozu, przeglądarkowej gry cRPG napisanej w Django. Paczka jest w wersji anglojęzycznej (częściowo wprowadzone i18n), ale spokojnie można się tym pobawić, przejrzeć kod i podzielić się wrażeniami. Gra jest daleka od ukończenia, jeszcze wiele szczegółów trzeba zaimplementować itd. Paczka dostępna jest na licencji Creative Commons BY NC SA i dalszy publiczny rozwój uzależniony jest od odzewu innych programistów :) Polskie demon dostępne jest w Bibliotece cRPG - wystarczy się zalogować i gotowe. Pakiet zawiera ikony Legendora, grafiki z dreamweavedworlds.com, a także z Baldurs Gate, Icewind Dale (używane tymczasowo w obecnej fazie rozwoju), które mają własne licencje.

ice1.png
ice2.jpg
ice3.jpg
ice4.jpg

Wyspa Mrozu ZIP

June 25, 2009 01:29 PM

June 22, 2009

Mateusz Zeromski

Python okiem programisty PHP - kompletne porównanie

Moja przygoda z pytonem zaczęła się w lutym 2008 i zakończyła się w czerwcu 2008. Szkic tego artykułu powstał pół roku temu, dziś mam nadzieję go ukończę i opublikuję. Zainteresowałem się tą technologią (python) z nadzieją, iż moje aplikacje będą o kilka poziomów lepsze. Aktualna praca pozwalała mi na małe R&D, więc po uzgodnieniu z [...]

June 22, 2009 07:47 PM

Jarosław Zabiełło

Scala - język przyszłości

Kiedy zapytano Jamesa Goslinga (twórcę Javy) o to, który z języków programowania współpracujących z JVM (wirtualną maszyną Javy) by użył teraz, pomijając samą Javę, odpowiedź była zaskakująco szybka i bardzo jasna – Scala.

Nazwa “Scala” pochodzi od “Scalable language” (język skalowalny). Język ten nadaje się równie dobrze do krótkich, zwartych skryptów jak i do tworzenia wydajnych, ogromnych, bezpiecznych systemów sieciowych. W swych założeniach Scala nawiązuje do minimalizmu składni Lispa i Smalltalka (większość rzeczy oparta jest na bibliotekach a nie na składni) dzięki czemu język ten praktycznie nie ma ograniczeń rozwoju i doskonale się skaluje (w miarę potrzeb można tworzyć nowe typy i całe nowe struktury wyglądające jak nowa składnia języka).

Scala jest językiem kompilowanym do bytecodu JVM dzięki czemu potrafi się integrować w sposób praktycznie przezroczysty, z całą platformą Javy (istnieje co prawda implementacja Scali dla platformy .NET ale jest jeszcze niedojrzała). W Scali mamy też wygodną konsolę do interaktywnego testowania kodu (tak jak to jest w Pythonie i Ruby). W Scali każda wartość jest obiektem, każda funkcja zwraca wartość. Zatem każda funkcja też jest obiektem (first class object). Funkcje są też obiektami wyższego rzędu (higher order kinds), można je zagnieżdżać, przekazywać w parametrach, a nawet stosować mechanizm dziedziczenia.

Java jako platforma i jako język

Słowo “Java” oznacza z jednej strony język programowania, z drugiej platformę JVM. Sam język Java jest co prawda prosty, ale też dosyć toporny. Nic dziwnego, że pada wiele słów krytyki pod jego adresem. Co innego platforma Javy. JVM jest dopracowana, stabilna i szybka (ze swoim HotSpotem potrafi pobić kod napisany w języku C). Biblioteki są dojrzałe i używane masowo w biznesie i instytucjach finansowych. Każda nowość błyskawicznie znajduje swoje odzwierciedleniew Javie (ilość projektów w Javie wielokrotnie przewyższa to, co jest dostępne dla platformy .NET). Platforma Javy mocno prze do przodu i zwiększa stale swoją pozycję. Wydaje się tylko, że sam język nie za bardzo nadąża za tymi zmianami.

Gdy Microsoft “wyskoczył” ze swoją koncepcją .NET umożliwiającą tworzenie aplikacji na tą platformę w kilku językach a nie “jedynym prawidłowym”, można było odebrać to jako prztyczek w nos dla starej doktryny lansowanej kiedyś w środowisku Javy (jako jednego języka do wszystkiego). W rzeczy samej, patrząc dziś, Microsoft ze swymi językami zgodnymi z CLR niczym specjalnym nie zachwyca. Po pierwsze, istnieje dziś już ok. 240 różnych języków zgodnych z platformą Javy (jakieś 5x tyle co dla CLR). Javowe implementacje powstały nawet do języków dynamicznie typowanych (Groovy) oraz takich jak Python (Jython), Ruby (JRuby), JavaScript (Rhino) czy PHP (Quercus).

Po drugie, w przeciwieństwie do .NET, Java jest prawdziwie multiplatformowa (istnieje co prawda projekt Mono, ale wciąż odstaje jakością od windowsowego .NET). Mówiąc krótko, platforma Javy trzyma się mocno. Ale sam język jest przestarzały i wszyscy się zastanawiają nad tym, co by mogło go zastąpić. Moim zdaniem, coraz więcej wskazuje, że tym językiem może być Scala. (Swoją drogą język Java tak całkiem to pewnie nigdy nie zniknie. Raczej zredukuje się do miana assemblera XXI wieku. Zresztą już dziś tak wielu myśli o Javie, jako o assemblerze dla JVM)

Scala jako pure OOP

Scala jest językiem obiektowym w stopniu większym niż Java. W Scali nie ma sztucznego podziału na obiekty i typy prymitywne (int, float, string). Każda wartość jest w Scali pełnym obiektem (choć pod spodem kompilator Scali, tam gdzie się da, sam dokonuje inteligentnych konwersji na javowe prymitywy).

Każdy operator jest też metodą. Np. zapis 1 + 2 odpowiada dokładnie temu samemu co (1).+(2). Innymi słowy obiekt “1” używa tu metody ”+” operującej na parametrze będącym obiektem “2”. A to, że można opuścić nawiasy związane jest z tzw. notacją operatorową jaka jest możliwa Scali. O ile Ruby pozwala opuszczać nawiasy w niektórych sytuacjach (w definicji i wywołaniu metody), to Scala pozwala na opuszczanie ich w wypadku każdej metody pomiędzy dwoma wyrażeniami. W efekcie Scala pozwala na pisanie testów BDD przypomijących jeszcze bardziej język naturalny niż RSpec do Ruby’ego (też scalaz, scalatest). Także tworzenie nowych języków specjalnego zastosowania, tzw. DSL’i, dzięki notacji operatorowej wyglądają w Scali ładniej niż w Ruby. W ramach żartu pokazującego elastyczność składni Scali napisano nawet interpreter języka BASIC.

W Scali, w przeciwieństwie do Javy, nie ma też statycznych metod ani zmiennych (żałosne jest to, że tak nieobiektowe konstrukcje w ogóle wprowadzono do PHP). Zamiast nich Scala używa singletonów i metod klasowych. Oczywistą przewagą takiego podejścia jest m.in. możliwość dziedziczenia.

Podczas gdy Ruby i Python trzymają definicje metod klasowych i instancji razem, Scala trzyma je zgrupowane oddzielnie.

# Python:
class C(Object):
    def metoda_instancji(self):
        return "Jestem metodą instancji" 
    @classmethod
    def metoda_klasowa(cls):
        return "Jestem metodą klasową" 
print C.metoda_klasowa()
print C().metoda_instancji
# Ruby:
class C
  def self.metoda_klasowa
    "Jestem metodą klasową" 
  end
  def metoda_instancji
    "Jestem metodą instancji" 
  end                       
end
puts C.metoda_klasowa
puts C.new.metoda_instancji
# Scala
class C {
  def metoda_instancji = 
    "Jestem metodą instancji"    
}          
object C {
  def metoda_klasowa = 
    "Jestem metodą klasową" 
} 
println((new C).metoda_instancji)
println(C.metoda_klasowa)         

Scala jako język statycznie typowany z inteligentną inferencją typów

Scala jest językiem statycznie typowanym. Z pewnością ucieszy to tych, co nie chcą pisać tony dodatkowych testów jednostkowych. Statyczna kontrola typów zapewnia większą kontrolę nad kodem. Można się zdziwić, jakim cudem przedstawiony wyżej kod działa, skoro nie ma w nim żadnej deklaracji typów. Właściwie to powyższa deklaracja klas i singletonu formalnie wygląda tak

class C {
  def metoda_instancji(): String = 
    return "Jestem metodą instancji";
}          
object C {
  def metoda_klasowa(): String = 
    return "Jestem metodą klasową";
} 

Jednak nie trzeba pisać tak rozwleklego kodu. Scala posiada wyrafinowany mechanizm inferencji typów pozwalający na ograniczenie niepotrzebnego deklarowania typów w wypadkach kiedy kompilator jest w stanie to sobie samemu wywnioskować (podobną inferencję typów stosują inne języki, np. Haskell).

W Scali także (podobnie jak w Ruby czy JavaScript) końcowe średniki zamykające wyrażenie można pominąć, nawiasy dla metod bez parametrowych są opcjonalne i nie trzeba używać komendy return aby zwrócić wartość z metody. W Scali (podobnie jak w Ruby) zawsze zwracana jest ostatnia wartość wyrażenia.

Scala, podobnie jak Java i Ruby, nie posiada dziedziczenia wielobazowego. W przeciwieństwie do Javy, Scala nie posiada interfejsów. I bardzo dobrze, bo ich przydatność jest wysoce wątpliwa (oczywiście do PHP5 dodano bezmyślnie te nieszczęsne interfejsy). Ich główną wadą jest to, że nie mogą posiadać żadnej implementacji ani deklaracji zmiennych. Z kolei klasy abstrakcyjne w Javie mogą posiadać częściową implementację, ale nie można dziedziczyć po wielu klasach abstrakcyjnych tylko po interfejsach które nie mają żadnej implementacji. Bardzo niewygodne. Scala rozwiązuje ten problem za pomocą traits. Traits działają podobnie do modułów w Ruby. Nie mają konstruktorow ani nie można stworzyć ich instancji więc odpada efekt konfliktu kolejności konstruktorów. Traits mogą jednak podlegać dziedziczeniu, mogą mieć częściową lub pełną implementację metod, oraz mogą posiadać zadeklarowane zmienne. I każda klasa może dziedziczyć po kilku traitsach. Proste i wygodne.

Niejawne konwersje (implicit conversion)

Idea niejawnych konwersji nie jest w zasadzie niczym nowa. Występuje już w języku C. Gdy dodawane są dwie zmienne o typach short i long to ta pierwsza zostaje domyślnie przekształcona do long. Nie trzeba wykonywać żadnego jawnego rzutowania typu.

Scala idzie dalej. Za pomocą mechanizmu implicit conversions potrafi uzyskać efekty podobne jak w Ruby, ale bardziej bezpieczne. Zobaczmy jednak wpierw jak to się robi Ruby (który wykorzystuje swoją koncepcję otwartych klas).

class Integer
  def kb
    self * 1024
  end     
  def mb
    self * 1024 * kb
  end  
end                   

class String 
  def welcome
    "Hello " + self
  end
end

puts 10.kb # => 10240
puts 10.mb # => 10485760
puts "Jarek".welcome # => Hello Jarek

Co prawda Ruby potrafi “zamrozić” swoje obiekty uniemożliwiając ich modyfikację

Fixnum.freeze

class Integer
  def kb
    self * 1024
  end
end   
# => TypeError: can't modify frozen class                     

ale technika ta (zwana też trochę niepoprawnie jako “monkey patching”) jest używana dosyć powszechnie w Ruby. Główną jej wadą jest to, że takie zmiany w klasach są globalne i to może prowadzić do różnych nieporozumień w innych częściach kodu.

Scala nie ma tego problemu. Pozwala na uzyskanie podobnych efektów bez konieczności globalnych zmian w obiekcie. Zamiast otwierania klas, Scala dodaje definicje niejawnych konwersji. Jeśli wywołujemy metodę obiektu której on nie posiada, Scala patrzy na leksykalny kontekst definicji dostępnych konwersji dla danego typu (jak pamietamy, Scala jest językiem statycznie typowanym). Jeśli znajdzie sygnaturę metody taką jak nazwa wywoływanej metody, użyje jej tak jakby należała do obiektu.

class StrConvert(s: String) {
  def welcome = "Hello " + s
}                         

class IntConvert(x: Int) {
   def kb = 1024 * x
   def mb = x * 1024 * kb
}                    

implicit def str_convert(s: String) =  new StrConvert(s)
implicit def int_convert(i: Int) =  new IntConvert(i)

println(10.kb)
println(2 mb)
println("Jarek" welcome)

Kropki w wywołaniach metod są oczywiście w Scali opcjonalne ze względu na notację operatorową.

Niejawne konwersje Scali przydają się w wypadku korzystania z cudzego kodu. Nie musimy (tak jak w Ruby) ingerować i zmieniać cudzych bibliotek. Zamiast tego wystarczy dodać lokalne definicje niejawnych konwersji typów. Przykładem toporności i ograniczeń Javy jest konieczność korzystania z innej składni do operacji na typach BigInteger. Jeśli początkowo pisaliśmy kod używający zwykłych typów int to w wypadku kiedy musi on operować na większych liczbach, czeka nas niezła refaktoryzacja kodu a kod mógłby wyglądać mniej więcej tak jak poniższy kod (przykład dotyczy składni Scali w ktorej też można pisać po “javowemu”)

                        
import java.math.BigInteger 

def factorial(x: BigInteger): BigInteger = 
 if (x == BigInteger.ZERO) 
   BigInteger.ONE 
 else 
   x.multiply(factorial(x.subtract(BigInteger.ONE)))      

Dzięki możliwościom implicit conversion, znacznie wygodniej jest pisać tak:

def factorial(x: BigInt): BigInt = 
 if (x == 0) 1 else x * factorial(x - 1) 

Warto zwrócić uwagę, że to nie jest jakaś dodana nowa składnia do Scali. Wrapper do BigInt jest stworzony w bibliotece. Nie trzeba żadnego przebudowania kodu Scali tak, jak to musi być uczynione w innych językach.

Scala jako język funkcyjny (FP)

Mam wrażenie, że programowanie funkcyjne przeżywa jakiś swój renesans. Od jakiegoś czasu więcej słychać o Erlangu, Clojure (świetny dialekt Lispa w Javie), Haskellu czy F#. Zalety programania funkcyjnego w stosunku do programowania imperatywnego są znane. Przede wszystkim, brak zmiennych i brak efektów ubocznych. Odpadają problemy związane z synchronizacją wątków, semaforami i blokowaniem dostępu do wspólnych obszarów pamięci. Kod pisany funkcyjnie znacznie łatwiej jest też testować. Funkcja niczym ma tylko jedno wejście i jedno wyjście. Nic innego ukradkiem nie zmieni. Programowanie funkcyjne jest też prostsze, choć osoby spaczone latami spędzonymi na programowaniu imperatywnym, mogą mieć na początku pewne problemy z przestawieniem sposobu myślenia. Np. w typowo funkcyjnym podejściu nie ma w ogóle pętli w programie. Zastępuje ją rekursja (która nie przepełnia stosu, jest to tzw. tail recursion)

Przykład różnic w podejściu imperatywnym oraz funkcyjnego dla Ruby’ego i Scali.

Ruby:

# Podejście imperatywne:

class Imper
  attr_accessor :x, :y
  def initialize(x,y)
    @x, @y = x, y
  end     
  def move_by(dx, dy)
    @x += dx
    @y += dy
  end
  def show
    puts "x:#{x}, y:#{y}" 
  end
end                                                                

x = Imper.new(1,2)
x.move_by(3,4)
x.show

# To samo funkcyjnie:

class Func
  attr_reader :x, :y
  def initialize(x,y)
    @x, @y = x, y
  end     
  def move_by(dx, dy)
    Func.new(@x+dx, @y+dy)
  end
  def show
    puts "x:#{x}, y:#{y}" 
  end
end

x = Func.new(1,2)
x = x.move_by(3,4)
x.show        

# lub to samo, ale bez tworzenie zmiennych:

Func.new(1,2).move_by(3,4).show

Scala:

                          
// Kod w stylu imperatywnym:

class Imper(x: Int, y: Int) {
  var iks: Int = x
  var igrek: Int = y           
  def move_by(dx: Int, dy: Int) {
    iks += dx
    igrek += dy
  }
  def show {
    println(iks,igrek)
  }
}                 
val p = new Imper(1,2)
p.move_by(3,4)
p.show

// i to samo funkcyjnie:

class Func(x: Int, y: Int) {
  val iks: Int = x
  val igrek: Int = y           
  def move_by(dx: Int, dy: Int) = new Func(iks+dx, igrek+dy)
  def show {
    println(iks,igrek)
  }
}                 
(new Func(1,2)).move_by(3,4).show

Obiekty deklarowane jako val są niemutowalne, nie można zmienić ich wartości (odpowiadają javowej deklaracji final). Z kolei deklaracja var oznacza zmienne mutowalne, mogące zmienić swoją wartość.

Kod pisany funkcyjnie, może być znacznie krótszy, prostszy i tym samym mniej podatny na błędy. Zobaczmy, jak bardzo można zredukować w Scali kod napisany w stylu imperatywnym do funkcyjnego (przykład z książki “Programming in Scala”)

                          
def printArgs(args: Array[String]): Unit = { 
  var i = 0 
  while (i < args.length) { 
    println(args(i)) 
    i += 1 
  } 
}

Pozbywamy się zmiennej var:

                          
def printArgs(args: Array[String]): Unit = { 
  for (arg <- args) 
    println(arg) 
} 

To samo, inaczej wyrażone:

                          
def printArgs(args: Array[String]): Unit = { 
  args.foreach(println) 
} 

Metoda nie jest jeszcze “czysto” funkcyjna bo posiada efekt uboczny w postaci drukowania wartości w funkcji println. Pozbywamy się więc i jej.

                          
def formatArgs(args: Array[String]) = args.mkString("\n") 

Wywołanie:

                          
println(formatArgs(args))

Warunki zwracające wynik

W Scali warunki logiczne także zwracają wartość. Weźmy na przykład kwestię operatora trynarnego. W wielu językach można spotkać (odziedziczoną po C) następującą składnię:

                          
wynik = warunek ? wyrazenie1 : wyrazenie2

Przez wiele lat w Pythonie trzeba było pisać w taki sposób:

                          
if warunek:
    wynik = wyrazenie1
else:
    wynik = wyrazenie2

Pod naciskiem społeczności, Guido van Rossum dodał w końcu odpowiednik takiej konstrukcji i teraz można pisać tak:

                          
wynik = warunek if wyrazenie1 else wyrazenie2

Tymczasem w Scali taki problem jest zupełnie sztuczny. Skoro warunek zwraca wynik, to nie trzeba wymyślać żadnej nowej składni naśladującej język C. Wystarczy po prostu napisać:

                          
val wynik = if (warunek) wyrazenie1 else wyrazenie2

Ogólnie, cała Scala promuje podejście funkcyjne, ale nie zmusza do niego. Scala nie stosuje oddzielnych nazw dla wersji mutowalnej i niemutowalnej. Dzięki temu unika się niepotrzebnego refactoringu kodu w wypadku potrzeby zmiany jednej wersji na drugą.

Domyślnie typy są niemutowalne.

                          
scala> val x = Map[Int, String]() 
# => x: scala.collection.immutable.Map[Int,String] = Map()

Aby mieć tym mutowalny, trzeba to jawnie wskazać.

                          
scala> import scala.collection.mutable.Map
scala> val x = Map[Int, String]()         
# => x: scala.collection.mutable.Map[Int,String] = Map()

Lift – komponentowy framework w Scali

Mówiąc o Scali nie sposób nie wspomnieć o świetnym frameworku webowym jakim jest Lift. Framework ten inspirowany jest różnymi frameworkami (głównie Wicket’em ale też Django, Rails czy smalltalkowym Seaside). Lift jest przede wszystkim frameworkiem komponentowym (odcina się tu od klasycznego modelu MVC promowanego przez Rails). Podejście komponentowe ma przewagę nad MVC, dużo lepiej się nadaje do złożonych aplikacji (innym przykładem dobrego podejścia komponentowego jest pythonowy CMSPlone. Zobacz też SweetScala – konkurencyjny framework w Scali)

Podsumowując.

Scala to nowoczesny język będący hybrydą pure OOP i FP, statycznie typowany ale z inteligentną inferencją typów, o bardzo elastycznej składni i kompilowana do bytecodu JVM. Dzięki zaawansowanej inferencji typów i implicit conversions nie odczuwa się narzutu związanego z faktem że Scala jest statycznie typowana (a dostaje zalety ściślejszej kontroli kodu). Scala nie ma operatorów, operatory są zwykłymi metodami definiowanymi w bibliotekach.

Jednak tym co jest największym atutem Scali to jej minimalizm składniowy. Scala umożliwia tworzenie nowych typów i struktur językowych bez konieczności zmian w samej zasadniczej składni języka. To oznacza, źe podobnie jak Lisp, Scala nie ma praktycznie żadnych ograniczeń w rozwoju. Tym samym Scala jest w stanie przetrwać różne tendencje i upodobania składniowe i “przeskalować” się do nowych potrzeb.

Scalę używa już gdzieś parę banków w UK. Na Scalę niedawno przeszedł Twitter, ma wkrótce przejść też Github.

Linki



by Jarosław Zabiełło at June 22, 2009 09:04 AM

June 21, 2009

Riklaunim

Nowe tutoriale dla Pylonsa

Ostatnio pojawiły się w sieci dwa tutoriale dotyczące frameworka Pylons z wykorzystaniem "nowych" technologii. Pierwszy z nich: Pylons, PostgreSQL, and Simpycity in 60 Minutes or Less dość szczegółowo opisuje wykorzystanie Simpycity przy tworzeniu serwisu www. Simpycity to biblioteka Pythona mapująca zapytania i procedury PostgreSQL do prostych wywołań w Pythonie. Druga publikacja, a właściwie kod źródłowy to prosty "blog" w MongoDB :) Jest to nierelacyjna baza danych dokumentów podobna do CouchDB, lecz napisana w C++.

June 21, 2009 09:50 AM

Google App Engine Python SDK 1.2.3 z obsługą Django 1.0

W piątek wydano kolejną wersję GAE SDK dla Pythona dodając obsługę Django 1.0 (nie trzeba go już przesyłać wraz z projektem), a także eksperymentalne kolejkowanie i wykonywanie w tle zadań (Task Queue). Także zgodnie z planem jutro powinny zostać zmniejszone limity dla "darmowych" kont do 6,5 godziny czasu procesora dziennie i 1 GB danych wysłanych i odebranych przez aplikację.

June 21, 2009 09:23 AM

Jak Pownce skalowało aplikacje Django

Mike Malone w prezentacji opisuje różne metody skalowania aplikacji Django, jakie były stosowane w pownce.com, webowym komunikatorze zbudowanym z wykorzystaniem technologii Adobe AIR jak i Django.

June 21, 2009 09:09 AM

June 19, 2009

Riklaunim

&quot;Django, Ćwiczenia&quot; już niebawem...

"Django, ćwiczenia", książka mojego autorstwa powinna niebawem trafić do sklepów (przynajmniej mam taką nadzieję :)). 88 stron to wprowadzenie do Django na bazie prostej aplikacji, rozbudowywanej krok po kroku prezentując kolejne elementy frameworka. Oprócz Django znajdzie się też trochę o GAE i Django + GAE.

June 19, 2009 02:19 PM

June 18, 2009

Riklaunim

Biblioteka standardowa Pythona w Native Client

Mark Seaborn donosi na swoim blogu o udanym wykorzystaniu biblioteki standardowej wewnątrz przeglądarki za pomocą Native Client. Jest to jak na razie rozwiązanie mocno "deweloperskie", ale z czasem może dać ciekawe możliwości przy konstruowaniu aplikacji webowych.

June 18, 2009 03:22 PM