Du må være registrert og logget inn for å kunne legge ut innlegg på freak.no
X
LOGG INN
... eller du kan registrere deg nå
Dette nettstedet er avhengig av annonseinntekter for å holde driften og videre utvikling igang. Vi liker ikke reklame heller, men alternativene er ikke mange. Vær snill å vurder å slå av annonseblokkering, eller å abonnere på en reklamefri utgave av nettstedet.
  7 2854
For å få litt mer aktivitet i programmeringsforumet, som for tiden er relativt dødt, tenkte jeg det kunne være en ide og prøve å starte opp litt gøye tråder. Jeg har allerede en compo kjørende - og vil nå bidra en med liten tråd som kanskje kan være av litt mer almenne interesse.

Jeg ønsker her at vi skal dele små tips og triks - og kanskje enkelte idiomer i forskjellige språk. For at dette ikke bare skal bli enda en ubrukelig listetråd er det derfor ekstra kult om det du deler er noe du kanskje tror mange ikke vet om. Prøv å hold inleggene veldig korte og konsise, men et par setninger forklarer et konsept er greit å få med. Korte kodeeksempler er også veldig godt motatt.


Easier to ask for forgiveness than permission
I mange språk er det vanlig å teste om noe funker før man prøver å gjøre det. F.eks. validere om inputen til en funksjon er gyldig før man kaller den. I python gjør man det motsatt: men anntar et ting går greit, og om det ikke gjør det så håndterer man det etterpå. Dette henger også veldig sammen med begrepet duck-typing.

Eksempel: La oss si du har en funksjon som et et objekt som argument. Denne funksjonen er ment til å ta i bruk objekter av typen Duck, og vil kalle quack() på dette objektet. Men også objekter som har arvet fra Duck godtar du. En måte å gjøre dette på er da:

Kode

class Duck:
    def quack(self):
        print "Quack quack!"

def my_function(duckobj):
    if isinstance(duckobj, Duck):
        duckobj.quack()
    else:
         # Handle error
Men siden man i python ønsker duck-typing burde ikke krav om arv eksisterer, men heller at at den har ønsket funksjonalitet. Så å sjekke arv, blir lite pythonisk. En bedre måte kan derfor være å sjekke eksistensen av quack-metoden som vi forventer:

Kode

def my_function(duckobj):
    if hasattr(duckobj, "quack"):
        duckobj.quack()
    else:
         # Handle error
Men dette kunne vært gjort finere med å bare bruke exceptions, som jeg selv mener lager koden mer lesbar

Kode

def my_function(duckobj):
    try:
        duckobj.quack()
    except (AttributeError, TypeError):
        # handle error, he can't quack!
Sist endret av etse; 23. juni 2014 kl. 18:22.
Et tips er en ny attributt i .net i namespacet System.Runtime.CompilerServices. CallerMemberName. Den brukes i et optional parameter, som kompilatoren setter inn navnet fra kalleren som argument for hvis det mangler. Spesielt hendig når det kommer til å implementere INotifyPropertyChanged-interfacet som brukes flittig i wpf.

Kode

        public class NotificationObject : INotifyPropertyChanged
        {
            protected void SetProperty<T>(ref T field, T value, [CallerMemberName] string property = "")
            {
                if (!field.Equals(value))
                {
                    field = value;
                    RaisePropertyChanged(property);
                }
            }

            private void RaisePropertyChanged(string propertyName)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                }
            }

            public event PropertyChangedEventHandler PropertyChanged;
        }

        public class Number : IEnumerable
        {
            public int Value { get; set; }
            public void Add(int a)
            {
                Value += a;
            }

            public IEnumerator GetEnumerator()
            {
                throw new NotImplementedException();
            }
        }
Vet ikke om det hører til her, men C# har faktisk en form for duck typing. Det gjelder måten collection initializers er implementert på. Nå er det rett nok bare halvveis, for klassen må implementere IEnumerable, men i og med at void Add(..) som faktisk brukes av Collection initializeren ikke er en del av interfacet er det litt funky likevel.

Kode

        [TestMethod]
        public void TestMethod1()
        {
            var num = new Number() { 1, 2, 3, 4 };
            Assert.AreEqual(10, num.Value);
        }

        public class Number : IEnumerable
        {
            public int Value { get; set; }
            public void Add(int a)
            {
                Value += a;
            }

            public IEnumerator GetEnumerator()
            {
                throw new NotImplementedException();
            }
        }

Edit:
Et lang mer innlysende er selvsagt Dynamics, men Collection initializers er en slags statisk duck typing
Sist endret av meitemark; 25. juni 2014 kl. 02:59. Grunn: Automatisk sammenslåing med etterfølgende innlegg.
NOOOOOOOOOOOOOOOOOO-
robhol's Avatar
Hva mener du egentlig med duck typing i dette tilfellet? Mulig jeg bare er trøtt, men jeg ser ikke helt likheten/poenget.
Ukjent
Trådstarter Donor
^ Tror du har trykket feil av siter og legg-til når du skrev den siste linjen. I tilleg sliter jeg litt med å forstå hva du prøver å få frem med at C# har dyck-typign i det eksempelet, det virker litt merkelig. Om du ønsker duck-typing i C# kan du bruke dynamic, som forklart i wikipedia-artikkelen. Men i et statisk type språk ville jeg prøvd å unngå det.

------------

Noen av dere har kanskje sett lambda-funksjoner i python og kanskje syntes det virket litt magisk? Jeg synes i hvertfall det virket litt skummelt, og forstod aldri helt bruken av det i begynnelsen.

Lambda-funksjoner er små funksjoner, gjerne på 1 enkel statement som man lager "on-the-fly" for å løse enkle oppgaver. F.eks. når du skal kalle en funksjon, hvor et av argumentene er en funksjonepeker, og du ønsker å legge ved en enkel one-liner så kan dette være perfekt.

Syntaxen til lambda er følgende:

Kode

lambda [arguments]: statement

# Følgende 2 ting ville vært det samme:
compare = lambda a, b: a>b

def compare(a, b):
    return a>b
De 3 plassene jeg har brukt dette mest er i sort, filter og map.

sortere en liste av tupler basert på gjennomsnittet av verdiene:

Kode

numbers = [(1, 5), (2, 6, 7), (10, 1, 132), (115, 1, 80)]
sorted_numbers = sorted(numbers, cmp=lambda a,b: sum(a)/len(a)>sum(b)/len(b))
Ta vare på tuplene som har et partall som sum:

Kode

numbers = [(1, 5), (2, 6, 7), (10, 1, 132), (115, 1, 80)]
even_sums = filter(lambda a: not sum(a) % 2, numbers)
Sist endret av etse; 24. juni 2014 kl. 01:42.
Var nok litt trøtt selv i går. Får ikke redigere innlegget lenger. Med ducktypingen mente jeg rett og slett at kompilatoren baserer seg på om metoden void Add(..) eksisterer for å avgjøre om det er en collection. Den bruker altså duck typing prinsippet til en viss grad - hvis den kan adde som en collection og er enumerable er det en collection vi kan bruke collection initializers på. Dette har sannsynligvis blitt gjort fordi det ienumerable er det vanligste og mest abstrakte interfacet, men det mangler add-metoden. Det var mer ment som en kuriositet, men det er jo kanskje litt nyttig å vite hva som skal til for å få collection initializers.
NOOOOOOOOOOOOOOOOOO-
robhol's Avatar
Men dette går vel bare dersom den aktuelle typen implementerer IEnumerable, og da er det jo ikke akkurat duck typing lenger.
Ukjent
Trådstarter Donor
Nå går tråden litt off-topic, men kan prøve å forklare hvorfor jeg mener det ikke er helt ducktyping. I python kan du lage en funksjon som tar som input et fil-objekt. Funksjonen vil lese gjennom fil-objektet linje for linje og gjøre noe med dataen. Men siden språket i seg selv ikke bryr seg om hva du sender inn (altså er ikke statisk typet) og aldri sjekker hva du faktisk har sendt inn kan du fint sende inn en file-socket. (altså en socket med abstraksjon som gjør at den har read og write på samme måte som en fil) - dette vil da kunne fungere helt fint.

I praksis kan man oppnå mye det samme i .NET med å f.eks. la funksjonen ta inn en stream, hvor du egentlig forventer en fil-stream, men det går helt greit å sende over en stream knyttet til en socket også. Forskjellen ligger bare i at i språk som .NET som vil man først sjekke at funksjonen har rett interface før man tar det i bruk, ved å sette krav om at det har implementert et bestemt interface eller arver fra en definert superklasse - mens i python velger du å bare prøve å gjøre det du ønsker, og om du ikke funker så håndterer du feilene når det skjer.

En av de største forskjellene kommer av hvordan språkene ser på exception. I python er exception sett på som en god ting. De mener det kan øke lesbarheten i koden, og er relativt sett ikke så veldig dyre med mindre de skjer hele tiden. I .NET (og mange andre språk) er derimot exceptions sett på som noe som kun skal brukes i helt spesielle tilfeller, gjerne hvor litt uforutsette ting skjer. En av grunnene til dette er at i .NET-verden er det å kaste en exception relativt mye dyrerere enn det er i python.
▼ ... over en uke senere ... ▼
Ukjent
Trådstarter Donor
Dobbel forløkker er noe man møter ofte i programmering, spesielt ofte når man jobber med flerdomensjonale tabeller eller matriser. Som selv om det kanskje høres litt matematisk ut, ofte er veldig vanlig når man behandler data. F.eks. om man har en tabell man kan skrive noe lignende dette:

Kode

for row in table:
    for cell in row:
        print cell.get_content_as_string()
Men jeg oppdaget nylig at python har en egen funksjon for akkurat dette: chain, som er en del av itertools.

Kode

from itertools import chain

for cell in chain(*table):
    print cell.get_content_as_string()
Det chain gjør er å lage en iterator som vil iterere over alle elementene i første argumentet den får inn - og når denne stopper vil den fortsette til neste argument. Og først når den er ferdig med det siste argumentet vil den stoppe (ved å kaste StopIteration)

Problemet med dette er om du har en stor tabell, så vil den måtte sende inn alle argumentene til funksjonen - noe som med den implementasjonen funksjonen har betyr at alle argumentene blir puttet i en liste som lagres i minne. Og iteratoren vil iterere over denne. Og de av dere som er litt kjent med idiomatisk python vil fort forstå at dette ikke er helt riktig måte å gjøre ting på.

Men som med mye annet, så er dette blitt løst i python >2.6. Det finnes en alternativ constructor chain.from_iterable() som tar inn et intererbart objekt (f.eks. en liste) som argument - og itererer over denne i stede. Dette vil spare mye ressurser og for store datasett være relativt mye raskere. Og kan brukes slik

Kode

from itertools import chain

for cell in chain.from_iterable(table):
    print cell.get_content_as_string()