mboost-dp1

Avanceret/elementært Python


Gå til bund
Gravatar #1 - arne_v
28. mar. 2024 17:38
Jeg er ikke god til Python så jeg ved ikke om det er avanceret eller elementært, men jeg blev overrasket da jeg så det på LinkedIn.


class X(object):
def __new__(clz, v):
print('new %s %d' % (clz.__name__, v))
return super().__new__(clz)
def __init__(self, v):
self.v = v
print('init %s %d' % (self.__class__.__name__, self.v))

X(123)

Gravatar #2 - larsp
29. mar. 2024 11:26
Jeg får umiddelbart:

new X 123
init X 123


__new__ har til formål at returnere en ny instans af en klasse.
__init__ instantierer den nye instans.

Hvad er det overraskende?

Jeg har aldrig haft behov for at implementere min egen __new__ i python kode. Jeg synes at man har gang i noget dirty business hvis man har brug for at ændre __new__s opførsel.

Python er pudsigt fordi der eksisterer et klasse "objekt" der bruges til at instantiere klasse instanser. En form for implicit factory pattern.
Gravatar #3 - arne_v
29. mar. 2024 23:28
#2

Det er muligvis ikke overraskende for dem som kender Python. Men det var overraskende for mig - jeg havde aldrig hørt om __new__ før.

Og jeg havde ikke forventet den slags i et sprog som Python.

Java, C# etc. kan ikke gøre sådan noget.

Det eneste sprog jeg kender som ellers kan gøre sådan noget er C++.


#include <iostream>
#include <cstdlib>

using namespace std;

class X
{
private:
int v;
public:
X(int v) { cout << "ctor " << v << endl; this->v = v; }
int GetV() { return v; }
void *operator new(size_t size) { cout << "new" << endl; return ::operator new(size); }
void operator delete(void *p) { ::operator delete(p); }
};

int main()
{
X *o = new X(123);
cout << o->GetV() << endl;
return 0;
}


Men jeg har aldrig overloadet new og delete i C++.



Gravatar #4 - arne_v
30. mar. 2024 00:44
#3

Og pinligt at jeg ikke kaldte delete ...
Gravatar #5 - larsp
30. mar. 2024 07:56
Ja, det er en pudsigt med __new__ funktionen. Jeg ser det som en konsekvens af at alt i python i virkeligheden er dictionaries. Hvordan gemmer man definitionen af en klasse? Den skal jo være et eller andet sted. Hvorfor ikke gemme klasse definitionen med samme teknik som andre objekter (dvs. reelt er et dictionary) og give den en new funktion. Som jeg skrev, et implicit factory pattern.

Man kunne vel misbruge __new__ til at lave mere specifikke factory regler. Men det vil være at misbruge systemet synes jeg og overraskende kode mange ikke vil forstå.
Gravatar #6 - larsp
30. mar. 2024 08:00
arne_v (3) skrev:
Det eneste sprog jeg kender som ellers kan gøre sådan noget er C++.

Aha, man kan overloade new og delete i C++. Shit. C++ er et monster. Tal lige om nogle footguns der kan laves med det.
Gravatar #7 - arne_v
30. mar. 2024 12:28
larsp (5) skrev:

Man kunne vel misbruge __new__ til at lave mere specifikke factory regler. Men det vil være at misbruge systemet synes jeg og overraskende kode mange ikke vil forstå.


Eksemplet jeg så op LinkedIn lave et check på om argumenter var valide. Tanken må være at lave det check inden man allokerer memory for objektet. Men det er så et rigtigt godt eksempel på premature opimization.
Gravatar #8 - arne_v
4. apr. 2024 00:53
larsp (6) skrev:
arne_v (3) skrev:
Det eneste sprog jeg kender som ellers kan gøre sådan noget er C++.

Aha, man kan overloade new og delete i C++. Shit. C++ er et monster. Tal lige om nogle footguns der kan laves med det.


Det er faktisk langt mere kompliceret end hvad det eksempel viste.

Først den anden meget forbedrede udgave af koden med new og delete i klassen.

test1.cpp:


#include <iostream>
#include <cstdlib>

using namespace std;

class X
{
private:
int v;
public:
X() : X(0) { }
X(int v) { cout << "ctor " << v << endl; this->v = v; }
virtual ~X() { cout << "dtor" << endl; }
int GetV() { return v; }
void *operator new(size_t size) { cout << "new" << endl; return ::operator new(size); }
void operator delete(void *p) { cout << "delete" << endl; ::operator delete(p); }
void *operator new[](size_t size) { cout << "new[]" << endl; return ::operator new[](size); }
void operator delete[](void *p) { cout << "delete[]" << endl; ::operator delete[](p); }
};

int main()
{
X *o = new X(123);
cout << "v = " << o->GetV() << endl;
delete o;
X *a = new X[2];
cout << "v = " << a[0].GetV() << endl;
cout << "v = " << a[1].GetV() << endl;
delete[] a;
return 0;
}


C:\Work\C>g++ test1.cpp -o test1.exe

C:\Work\C>test1
new
ctor 123
v = 123
dtor
delete
new[]
ctor 0
ctor 0
v = 0
v = 0
dtor
dtor
delete[]

Jeg synes ikke at det er så slemt. Udvikleren beder om noget opg får det.

Men men men.

(fortsættes)
Gravatar #9 - arne_v
4. apr. 2024 00:57
Men man kan også erstatte de globale new og delete.

test2.cpp:


#include <iostream>
#include <cstdlib>

using namespace std;

class X
{
private:
int v;
public:
X() : X(0) { }
X(int v) { cout << "ctor " << v << endl; this->v = v; }
virtual ~X() { cout << "dtor" << endl; }
int GetV() { return v; }
};

int main()
{
X *o = new X(123);
cout << "v = " << o->GetV() << endl;
delete o;
X *a = new X[2];
cout << "v = " << a[0].GetV() << endl;
cout << "v = " << a[1].GetV() << endl;
delete[] a;
return 0;
}


C:\Work\C>g++ test2.cpp -o test2.exe

C:\Work\C>test2
ctor 123
v = 123
dtor
ctor 0
ctor 0
v = 0
v = 0
dtor
dtor

hack.cpp:


#include <iostream>
#include <cstdlib>

using namespace std;

void *operator new(size_t size)
{
cout << "new" << endl;
return malloc(size);
}

void operator delete(void *p)
{
cout << "delete" << endl;
free(p);
}

void *operator new[](size_t size)
{
cout << "new[]" << endl;
return malloc(size);
}

void operator delete[](void *p)
{
cout << "delete[]" << endl;
free(p);
}


:\Work\C>g++ -c hack.cpp -o hack.obj

C:\Work\C>g++ test2.cpp hack.obj -o test2.exe

C:\Work\C>test2
new
ctor 123
v = 123
dtor
delete
new[]
ctor 0
ctor 0
v = 0
v = 0
dtor
dtor
delete[]

Yuck!!!!

Hvis det er to forskellige udviklere som har lavet test2.cpp og hack.cpp så undrer udvikleren af test2.cpp sig nok lidt.
Gravatar #10 - larsp
4. apr. 2024 07:39
Damn. Hvis alle udviklere kendte til alle C++ features og havde overmenneskelige evner (og overmenneskelig disciplin) ville det være det eneste sprog der var brug for.

Med min baggrund i C har jeg altid været imod at bruge komplicerede features i programmeringssprog og hældet til at eksplicit implementere funktionalitet.

Hvad gør koden? Læs koden. Det gælder for C hvis det er skrevet nogenlunde pænt. I C++ bliver det hurtigt meget svært hvis man har gang i advancerede objektorienterede features.
Gravatar #11 - arne_v
4. apr. 2024 13:40
#10

Nogle sprog er designet til at være simple.

C, Cobol, Fortran til og med 77, Java til og med 1.4 etc. er eksempler på sprog med et ønske om at være simple.

"Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away" (kendt citat)

Andre sprog (PL/I, Ada, C++, Scala etc.) er eksempler på sprog med et ønske om at kunne alt.

"principielt bør alt input compile - det er bare et spørgsmål om at få defineret semantikken" (selvopfundet)


Gravatar #12 - arne_v
9. apr. 2024 14:15
#10

Der er to forskellige tilgange til kode:
- man udtrykker hvad der logisk skal ske og compiler/runtime oversætter logikken til den måde computere fungerer på
- koden afspejler den måde computere fungerer på og man kan stort set hånd oversætte til pseudo-assembler hvis det skulle være

Eksempel på det første:

s = s + "X";

Logikken er at man tilføjer et X til strengen. Compiler/runtime må tage sig af alt det med memory håndtering.

Eksempel på det sidste:

temp = malloc(strlen(s) + 2);
strcpy(temp, s);
strcat(temp, "X");
free(s);
s = temp;

Koden håndterer eksplicit memory.

Og jeg kan se det som VAX assembler:

pushl s
calls #1, strlen
addl2 #2, r0
pushl r0
calls #1, malloc
movl r0, temp
pushl s
pushl temp
calls #2, strcpy
pushab xlit
pushl temp
calls #2, strcat
pushl s
calls #1, free
movl temp, s



Gravatar #13 - arne_v
9. apr. 2024 14:25
#12

Enkelte gang har man brug for at vide hvad der sker memory mæssigt.

VMS har visse regler for memory tilgang.

access violation (kendt som segment fault i andre OS):

user mode => application terminates
supervisor mode og executive mode => process is terminated
kernel mode => system crashes

Men i kernel mode kan man have forskellige IPL (Interupt Priority Level).

VMS bruger virtuelt memory, så forsøger man at tilgå noget virtuelt memory som ikke er i fysiske memory så allokeres der noget fysisk memory og enten læses data ind i det eller så initaliseres det bare. Helt transparent for koden.

Med en lille finesse. Den OS kode som håndterer det usynligt kører i kernel mode med et bestemt IPL. Hvis ens egen kode er i kernel mode med et højere IPL, så kan den OS kode ikke køre. Så crasher systemet.

Kernel mode kode med høj IPL er nødt til at få al det virtuelle memory det skal bruge loadet ind i fysisk memory inden det starter.

Det kan kun lade sig gøre med sprog hvor man har 100% styr på hvilken memory der bruges.

Gå til top

Opret dig som bruger i dag

Det er gratis, og du binder dig ikke til noget.

Når du er oprettet som bruger, får du adgang til en lang række af sidens andre muligheder, såsom at udforme siden efter eget ønske og deltage i diskussionerne.

Opret Bruger Login