Python?

"Ei sillä tietenkään ole mitään väliä tehtävän kontekstissa, mutta aloittelevat koodarit, jotka tehtäviä tekevät, alkavat luulla, että tuollainen täysin turha perusluokka pitäisi aina tehdä "hyvän tavan vuoksi" tai koska sillä saattaa olla jokin tekninen aloittelijalle tuntematon tekninen peruste. Olisi ollut aika helppoa keksiä tehtävänanto, jossa koodia ei tule juurikaan lisää mutta joka on ajatuksen tasolla paljon järkevämpi."

Tätä en epäile yhtään. Jos on tehnyt kirjan tehtävät tuohon mennessä, niin pitäisi ymmärtää tuo.


"Kysehän on siis kaksisuuntaisesta one-to-many-assosiaatiosta. Oikeassa ohjelmakoodissa se tarkoittaa sitä, että jos luokan A olio saa viitteen luokan B-olioon mutta ei toisin päin, niin ohjelmassa on hyvin todennäköisesti bugi ja siitä voi seurata esimerkiksi ohjelman kaatuminen tai kielestä riippuen vaikka muistivuoto."


Eli tässä teit tämän?

"Toimiva ohjelmakoodi olisi tällainen:

Python:
class Ping:
    def set_pong(self, pong):
        if not self.pong:
            self.pong = pong
            pong.add_ping(self)

class Pong:
    def add_ping(self, ping):
        if ping not in self.pings:
            self.pings.append(ping)
            ping.set_pong(self)
"

Yritin ajaa tehtävää koodillasi:
pong = Pong()
ping = Ping()
pong.add_ping(ping)

tulee virhe ilmoitus:
Koodi:
Traceback (most recent call last):
  File "/home/user/Työpöytä/delme.py", line 16, in <module>
    pong.add_ping(ping)
  File "/home/user/Työpöytä/delme.py", line 10, in add_ping
    if ping not in self.pings:
AttributeError: 'Pong' object has no attribute 'pings'
Mikähän mättää?

"Siis: sekä Ping- että Pong-luokan assosiaation rakentava metodi varmistaisi, että sekä Ping.pong että Pong.pings ovat aina ajan tasalla, eli että ei ole "orpoja" Ping-instansseja, joilla olisi viite Pong-olioon ilman että sama Pong-olio omaa viitteen kyseiseen Ping-olioon ja toisin päin."
Ei tuossa alkuperäisessä ole mitään orpoja instansseja?
pong-olio sisältää viitteitä rajattomasti pingiin ja samoin ping-olio pongiin.

"(Huomaa, että loputtoman rekursion välttämiseksi on metodeissa myös tarkistettava, että onko viite jo rakennettu, ennen kuin kutsutaan vastakkaisen luokan metodia. Ilman tarkistusta siitä seuraa ikuinen ping-pong-efekti. Oliko se sittenkin syy, miksi luokat on nimetty näin?)"
Jaahas taisit keksiä?


"Ensinnäkin sinä keksit ihan itse tuon "attribuuttijonolla" kikkailun. En nähnyt koko kirjassa muita viittauksia samaan harjoitukseen tai että samaa koodia olisi käytetty myöhemmin kirjassa."
On totta ettei tehtävänannossa pyydetty perehtymään ohjelman sielunelämään kuin kaavion verran, mutta jäi häiritsemään ohjelman kulku, joten lisäsin muutaman print()-rivin, en ole mitään keksinyt, ohjelman on ihan sama kuin kirjassa.

"Toisekseen oikeassa ohjelmakoodissa härpäkkeen tarkoitus ei varmastikaan olisi rakentaa hyvin arkista one-to-many-linkitystä vaan se olisi lähinnä vaatimus jonkin oikean toiminnallisuuden toteuttamiseksi, yksi monista välivaiheista."

No tämä ei sitten ollut se "oikea" ohjelma siihen tarkoitukseen.



"Miksei luokkia olisi voinut nimetä vaikka näin:

Python:
class Organism:
    pass

class Tree(Organism):
    def __init__(self):
        self.apples = []

    def add_apple(self, apple):
        pass

class Apple(Organism):
    def set_tree(self, tree):
        pass
"

tuossa pitäisi kai olla:
Koodi:
    def add_apple(self, apple):
        self.apples.append(apple)

"Miksei luokkia olisi voinut nimetä vaikka näin?"

Sen pingpong efektin takia?

"(Huomaa myös, ettei minun koodissani eikä alkuperäisessäkään esimerkissä kutsuta isäntäluokan konstruktoria aliluokasta vaikka niin pitäisi tehdä. Jälleen yksi aika iso virhe opetusmateriaalissa.)"

Tätä ei ole vielä kirjassa käsitelty. PingPongParent ei myöskään sisällä konstruktoria.
 
Yritin ajaa tehtävää koodillasi:
pong = Pong()
ping = Ping()
pong.add_ping(ping)

tulee virhe ilmoitus:
Koodi:
Traceback (most recent call last):
  File "/home/user/Työpöytä/delme.py", line 16, in <module>
    pong.add_ping(ping)
  File "/home/user/Työpöytä/delme.py", line 10, in add_ping
    if ping not in self.pings:
AttributeError: 'Pong' object has no attribute 'pings'
Mikähän mättää?
Koodi ei ollut kokonainen vaan havainnollistava esimerkki. Tosin siitä olisi saanut toimivan lisäämällä konstruktorin ja alustamalla siinä pings-jäsenmuuttujan tyhjäksi taulukoksi... Heitin koodin tähän bbs:n editoriin livenä ja testaamatta enkä siksi viimeistellyt sitä. Se esimerkki omenapuusta oli vieläkin karsitumpi ja laitoin siihen vain rajapinnan määrittelyn ilman metodien runkoa, koska voit kopioida sen toisesta esimerkistä.

"Siis: sekä Ping- että Pong-luokan assosiaation rakentava metodi varmistaisi, että sekä Ping.pong että Pong.pings ovat aina ajan tasalla, eli että ei ole "orpoja" Ping-instansseja, joilla olisi viite Pong-olioon ilman että sama Pong-olio omaa viitteen kyseiseen Ping-olioon ja toisin päin."
Ei tuossa alkuperäisessä ole mitään orpoja instansseja?
pong-olio sisältää viitteitä rajattomasti pingiin ja samoin ping-olio pongiin.
Ongelma ei ole rajaton viitteiden määrä vaan se, että aika usein kaksisuuntaisen assosiaation vaatimuksena on se, että yhteys on myös aina toimiva molempiin suuntiin. Aika usein kyse on parent-child-suhteesta, missä yhdellä vanhempioliolla on useita lapsiolioita. Lapsilla ei tietenkään voi olla vanhempaa, jos vanhempi ei samalla tiedä, ketkä sen lapsia ovat.

Tämä on nyt siis vain saivartelua, sillä yksisuuntainen assosiaatiokin on täysin hyväksyttävä malli ja se voi olla joko one-to-many tai many-to-one. Pingpong-esimerkin kohdalla nipotin asiasta siksi, että koodin perusteella vaikutti siltä, että Pong-luokan pings-taulukossa tulisi olla tieto kaikista niistä Ping-luokan instansseista, joilla on viite kyseiseen Pong-olioon. (Periaatteessa on mahdollista, että nämä kaksi viitettä ovatkin olemassa eri syistä ja silloin kyse ei ole kaksisuuntaisesta assosiaatiosta vaan kahdesta erillisestä yksisuuntaisesta assosiaatiosta.)

"(Huomaa, että loputtoman rekursion välttämiseksi on metodeissa myös tarkistettava, että onko viite jo rakennettu, ennen kuin kutsutaan vastakkaisen luokan metodia. Ilman tarkistusta siitä seuraa ikuinen ping-pong-efekti. Oliko se sittenkin syy, miksi luokat on nimetty näin?)"
Jaahas taisit keksiä?
Se oli vitsi.

"(Huomaa myös, ettei minun koodissani eikä alkuperäisessäkään esimerkissä kutsuta isäntäluokan konstruktoria aliluokasta vaikka niin pitäisi tehdä. Jälleen yksi aika iso virhe opetusmateriaalissa.)"

Tätä ei ole vielä kirjassa käsitelty. PingPongParent ei myöskään sisällä konstruktoria.
Konstruktori on aina olemassa (pythonissa), ts. voit kutsua kantaluokan konstruktoria aliluokasta vaikket olisi itse kirjoittanut sille toteutusta kantaluokkaan.

Konstruktoriin liittyvät säännöt vaihtelevat hyvin paljon ohjelmointikielen mukaan. C++:ssa olio ymmärtääkseni on epäkelvossa tilassa, jos isäntäluokan konstruktoria ei kutsuta. Php:ssä luokalla on konstruktori vain, jos se on eksplisiittisesti määritelty. Tällöin isäntäluokan konstruktoria ei saa kutsua, jos sitä ei ole kirjoitettu koodiin.

Pythonissa konstruktoria ei ole pakko kutsua mutta se on helppo tapa aiheuttaa yllättäviä bugeja ohjelman jatkokehityksen aikana. Kaikki ei-staattiset jäsenmuuttujat on luotava konstruktorissa, joten jos kantaluokalle lisätään myöhemmin omia jäsenmuuttujia, sille on lisättävä konstruktorikin. Mutta koska siitä periytyvät aliluokat eivät kutsu tätä konstruktoria, niin kantaluokan jäsenmuuttujia ei luoda näille aliluokan instansseille. Ja tällöin kyse on bugista.
 
Viimeksi muokattu:
Koodi ei ollut kokonainen vaan havainnollistava esimerkki. Tosin siitä olisi saanut toimivan lisäämällä konstruktorin ja alustamalla siinä pings-jäsenmuuttujan tyhjäksi taulukoksi... Heitin koodin tähän bbs:n editoriin livenä ja testaamatta enkä siksi viimeistellyt sitä. Se esimerkki omenapuusta oli vieläkin karsitumpi ja laitoin siihen vain rajapinnan määrittelyn ilman metodien runkoa, koska voit kopioida sen toisesta esimerkistä.


Ongelma ei ole rajaton viitteiden määrä vaan se, että aika usein kaksisuuntaisen assosiaation vaatimuksena on se, että yhteys on myös aina toimiva molempiin suuntiin. Aika usein kyse on parent-child-suhteesta, missä yhdellä vanhempioliolla on useita lapsiolioita. Lapsilla ei tietenkään voi olla vanhempaa, jos vanhempi ei samalla tiedä, ketkä sen lapsia ovat.

Tämä on nyt siis vain saivartelua, sillä yksisuuntainen assosiaatiokin on täysin hyväksyttävä malli ja se voi olla joko one-to-many tai many-to-one. Pingpong-esimerkin kohdalla nipotin asiasta siksi, että koodin perusteella vaikutti siltä, että Pong-luokan pings-taulukossa tulisi olla tieto kaikista niistä Ping-luokan instansseista, joilla on viite kyseiseen Pong-olioon. (Periaatteessa on mahdollista, että nämä kaksi viitettä ovatkin olemassa eri syistä ja silloin kyse ei ole kaksisuuntaisesta assosiaatiosta vaan kahdesta erillisestä yksisuuntaisesta assosiaatiosta.)


Se oli vitsi.


Konstruktori on aina olemassa (pythonissa), ts. voit kutsua kantaluokan konstruktoria aliluokasta vaikket olisi itse kirjoittanut sille toteutusta kantaluokkaan.

Konstruktoriin liittyvät säännöt vaihtelevat hyvin paljon ohjelmointikielen mukaan. C++:ssa olio ymmärtääkseni on epäkelvossa tilassa, jos isäntäluokan konstruktoria ei kutsuta. Php:ssä luokalla on konstruktori vain, jos se on eksplisiittisesti määritelty. Tällöin isäntäluokan konstruktoria ei saa kutsua, jos sitä ei ole kirjoitettu koodiin.

Pythonissa konstruktoria ei ole pakko kutsua mutta se on helppo tapa aiheuttaa yllättäviä bugeja ohjelman jatkokehityksen aikana. Kaikki ei-staattiset jäsenmuuttujat on luotava konstruktorissa, joten jos kantaluokalle lisätään myöhemmin omia jäsenmuuttujia, sille on lisättävä konstruktorikin. Mutta koska siitä periytyvät aliluokat eivät kutsu tätä konstruktoria, niin kantaluokan jäsenmuuttujia ei luoda näille aliluokan instansseille. Ja tällöin kyse on bugista.

Pitää varmaan palata tähän harjoitukseen vielä sen jälkeen kun tuo assosiaatio-asia on mullekin selvennyt, tiedä sitten tuleeko se edes tuossa kirjassa esille, koska siinä on enää jaljellä pari lukua. Enempää en osaa näillä tiedoilla kommentoida.
 
Attribuuttien lisääminen onnistuu objektin ulkopuoleltakin:

Koodi:
class Foo(object):
    pass

foo = Foo()
foo.a = 3

Propertyjäkin pystyy lisäämään, mutta ne pitää liittää itse luokkaan

Koodi:
Foo.b = property(lambda self: self.a + 1)
 
Tämä ei pidä paikkaansa. Luokalle voi luoda jäsenmuuttujia missä funktiossa tahansa.
Ilmaisin asiani huonosti mutta tarkoitin sitä, että monessa kielessä jäsenmuuttujat voi luetella luokan "rungossa" funktioiden rinnalla ja ne ovat olemassa automaattisesti. Pythonissa tämä ominaisuus on olemassa vain staattisille muuttujille siinä, missä instanssikohtaiset muuttujat pitää määritellä instanssin konstruktorissa tai perustellusta syystä jossain toisessa funktiossa tai kokonaan luokan ulkopuolellakin.
 
Luin yllä olevan keskustelun aikanaan enkä tajunnut juuri mitään. Luin nyt uudelleen virkeämpänä, ja en edelleenkään ymmärtänyt sen enempää. :bored:

Enpä ole vielä aiemmin nähnyt yhtä sekavaa, redundanttia ja rikkinäistä harjoitustehtävää...
...ja varmaankin nimenomaan tästä syystä. Ainoa syy, miksi tuota tehtävää voisi perustella on näyttää, että "ei näin".
 
Viimeksi muokattu:
Käsittääkseni siinä on ongelma että oliolla on attribuutti joka sisältää referenssin olioon itseensä. Mietin miten sen voisi tarkistaa ettei olion attribuuteissa ole referenssiä olioon itseensä ja muokkasin harjoitusta lisäämällä siihen tämän tarkistuksen tekevän metodin:
Koodi:
"""pingpong.py
Exercise 18.1 in book Think Python

link to book:
https://greenteapress.com/wp/think-python-2e/

Added method: has_self_in_attributes()
Comment out two related lines in add_ping() to see result of original code.
"""
from time import sleep

class PingPongParent:
    pass


class Ping(PingPongParent):
   
    def __init__(self, pong):      
        self.pong = pong


class Pong(PingPongParent):

    def __init__(self, pings=None):      
        if pings is None:
            self.pings = []
        else:
            self.pings = pings
           
    def add_ping(self, ping):
        if has_self_in_attributes(self, ping):  # comment out to test original code
            return                              # comment out to test original code
        self.pings.append(ping)


def has_self_in_attributes(one, another):
    """Check recursively attributes of another if they include reference to one.
    """
    attributes = [attr for attr in dir(another) if not attr.startswith("_")]
    #print(one,another,attributes)
   
    for attr in attributes:
        obj = getattr(another, attr)
        if obj == one:                  
            return True
        has_self_in_attributes(one, obj)
   
       
       
       

pong = Pong()
ping = Ping(pong)
pong.add_ping(ping)
"""
If not using has_self_in_attributes(), pong.add_ping(ping) leads to:
pong.pings = [ping] = [Ping(pong)] => ping.pong.pings => ping.pong.pings[0] = pong ..etc..
So object has reference to itself in its attribute. This leads to infinity(problem)
as demonstrated below.
"""

obj = ping.pong
obj_str = 'ping.pong'

try:  
    while True:

        attr = 'pings'
        obj = getattr(obj, attr)
        obj_str += '.pings[0]'
        if type(obj[0]) == Ping:
            print("Ping")
        else:
            break

        sleep(1)
       
        attr = 'pong'  
        obj = getattr(obj[0], attr)
        obj_str += '.pong'
        if type(obj) == Pong:
            print("Pong")
        else:
            break

        sleep(1)

except KeyboardInterrupt:
    print(obj_str + ": ", type(obj))
e: typo
 
Käsittääkseni siinä on ongelma että oliolla on attribuutti joka sisältää referenssin olioon itseensä. Mietin miten sen voisi tarkistaa ettei olion attribuuteissa ole referenssiä olioon itseensä ja muokkasin harjoitusta lisäämällä siihen tämän tarkistuksen tekevän metodin:
Koodi:
"""pingpong.py
Exercise 18.1 in book Think Python

link to book:
https://greenteapress.com/wp/think-python-2e/

Added method: has_self_in_attributes()
Comment out two related lines in add_ping() to see result of original code.
"""
from time import sleep

class PingPongParent:
    pass


class Ping(PingPongParent):
 
    def __init__(self, pong):    
        self.pong = pong


class Pong(PingPongParent):

    def __init__(self, pings=None):    
        if pings is None:
            self.pings = []
        else:
            self.pings = pings
         
    def add_ping(self, ping):
        if has_self_in_attributes(self, ping):  # comment out to test original code
            return                              # comment out to test original code
        self.pings.append(ping)


def has_self_in_attributes(one, another):
    """Check recursively attributes of another if they include reference to one.
    """
    attributes = [attr for attr in dir(another) if not attr.startswith("_")]
    #print(one,another,attributes)
 
    for attr in attributes:
        obj = getattr(another, attr)
        if obj == one:                
            return True
        has_self_in_attributes(one, obj)
 
     
     
     

pong = Pong()
ping = Ping(pong)
pong.add_ping(ping)
"""
If not using has_self_in_attributes(), pong.add_ping(ping) leads to:
pong.pings = [ping] = [Ping(pong)] => ping.pong.pings => ping.pong.pings[0] = pong ..etc..
So object has reference to itself in its attribute. This leads to infinity(problem)
as demonstrated below.
"""

obj = ping.pong
obj_str = 'ping.pong'

try:
    while True:

        attr = 'pings'
        obj = getattr(obj, attr)
        obj_str += '.pings[0]'
        if type(obj[0]) == Ping:
            print("Ping")
        else:
            break

        sleep(1)
     
        attr = 'pong'
        obj = getattr(obj[0], attr)
        obj_str += '.pong'
        if type(obj) == Pong:
            print("Pong")
        else:
            break

        sleep(1)

except KeyboardInterrupt:
    print(obj_str + ": ", type(obj))
e: typo

Eiii tämä senkus pahenee..! :nb:

Tehtävänannossa pyydettiin tekemään UML-kaavio, ja sitä varten tuo koodi lienee vielä ihan OK. Mutta mitään järkeä tuossa koodissa ei ole, ja sen ymmärtäminen vaatii vähintään kasan olettamuksia. Itse tykkään enemmän esimerkeistä, jotka kuvaavat jotakin "todellista" ongelmaa. FooBar/PingPong tyyppiset tehtävät ovat vain laiskuutta tehtävän suunnittelijalta.

Esimerkiksi alla olevasta koodista jo tajuaa, mistä on kyse (ja nyt voidaan alkaa pohtimaan, pitääkö osakkeella (Share) olla tieto siitä, kuka sen omistaa).

Koodi:
class PrintableObject(object):

    def __str__(self):
        return "This object is: {}".format(self.__class__.__name__)

class Share(PrintableObject):

    def __init__(self, company):
        self.company = company

    def __str__(self):
        return super().__str__() + " ...of company: " + self.company.name


class Company(PrintableObject):

    def __init__(self, name, holdings=None):
        self.holdings = holdings or []
        self.name = name

    def add_holding(self, holding):
        self.holdings.append(holding)

netflix = Company("Netflix")
google = Company("Google")

netflix.add_holding(Share(netflix))
netflix.add_holding(Share(google))
google.add_holding(Share(netflix))

print(netflix)
print(google)
print(netflix.holdings[0])

''' print result:
This object is: Company
This object is: Company
This object is: Share ...of company: Netflix
'''
 
Joo mä taidan vaan unohtaa ton pong-tehtävän. Sun esimerkki on paljon mielenkiintoisemman näköinen, kiitos siitä!
 
netflix.add_holding(Share(netflix))
Tavaan tätä riviä niin: tossa lisätään Share-objekti netflix-objektiin, ja tuo Share-objekti sisältää viittauksen netflix-objektiin muuttujassa company. Tää on ok? Mutta ei esim. että lisätään suoraan netflix:n muuttujaan holdings netflix itse: netflix.add_holding(netflix), vai onko tämäkin ok? Että objekti ns. sisältää itse itsensä? :confused:
 
Tavaan tätä riviä niin: tossa lisätään Share-objekti netflix-objektiin, ja tuo Share-objekti sisältää viittauksen netflix-objektiin muuttujassa company. Tää on ok? Mutta ei esim. että lisätään suoraan netflix:n muuttujaan holdings netflix itse: netflix.add_holding(netflix), vai onko tämäkin ok? Että objekti ns. sisältää itse itsensä? :confused:
Äärimmäisen hyvä kysymys, ja nimenomaan se, joka tässä aiheuttaa niin paljon päänvaivaa.

Tässä "tosielämän" esimerkissä tämä tarkottaisi, että kyseinen Share-osake on siis yksi Netflixin osake, jonka Netflix -yritys ostaa itselleen. Osakeyhtiöthän voivat omistaa osakkeita, sekä toisten yritysten että omiaan. Kun Netflix omistaa Netflix-osakkeita, voi yhtiö päättää tuhota kyseiset osakkeet (jolloin jäljelle jääneiden arvo nousee) tai myydä ne eteenpäin ja kasvattaa käteisvarallisuuttaan. (Tai kolmas vaihtoehto on vaihtaa ne esimerkiksi toisen yrityksen osakkeisiin).

neflix.add_holding(netflix) on tässä esimerkissä ehkä järjetön toimenpide. Tämähän tarkoittaisi, että Netflix-yritys omistaisi kokonaan Netflix-yrityksen. Ylipäänsä osakeyhtiöitä ei omisteta suoraan, vaan osakeyhtiöstä omistetaan osakkeet (toki yksittäinen taho voi omistaa 100% Netflixin osakkeista). Lisäksi oletan, että osakeyhtiölaki kieltää yritystä omistamasta kokonaan omaa osakekantaansa.

Sanon "ehkä", koska periaatteessa netflix.add_holding(google) voisi olla järkevä tilanteessa, jossa Netflix ostaa Googlen. Kuitenkin tässäkin kohtaa suosisin eksplisiittisempää muotoa: netflix.add_holdings(google.holdings). Tämä tarkoittaa, että Netflix ostaa Google-yrityksen sijaan Googlen omistamat osakkeet.

Järkevin syntaksi onkin riippuvainen siitä, mitä ongelmaa kulloinkin ratkaistaan.
 
Äärimmäisen hyvä kysymys, ja nimenomaan se, joka tässä aiheuttaa niin paljon päänvaivaa.

Tässä "tosielämän" esimerkissä tämä tarkottaisi, että kyseinen Share-osake on siis yksi Netflixin osake, jonka Netflix -yritys ostaa itselleen. Osakeyhtiöthän voivat omistaa osakkeita, sekä toisten yritysten että omiaan. Kun Netflix omistaa Netflix-osakkeita, voi yhtiö päättää tuhota kyseiset osakkeet (jolloin jäljelle jääneiden arvo nousee) tai myydä ne eteenpäin ja kasvattaa käteisvarallisuuttaan. (Tai kolmas vaihtoehto on vaihtaa ne esimerkiksi toisen yrityksen osakkeisiin).

neflix.add_holding(netflix) on tässä esimerkissä ehkä järjetön toimenpide. Tämähän tarkoittaisi, että Netflix-yritys omistaisi kokonaan Netflix-yrityksen. Ylipäänsä osakeyhtiöitä ei omisteta suoraan, vaan osakeyhtiöstä omistetaan osakkeet (toki yksittäinen taho voi omistaa 100% Netflixin osakkeista). Lisäksi oletan, että osakeyhtiölaki kieltää yritystä omistamasta kokonaan omaa osakekantaansa.

Sanon "ehkä", koska periaatteessa netflix.add_holding(google) voisi olla järkevä tilanteessa, jossa Netflix ostaa Googlen. Kuitenkin tässäkin kohtaa suosisin eksplisiittisempää muotoa: netflix.add_holdings(google.holdings). Tämä tarkoittaa, että Netflix ostaa Google-yrityksen sijaan Googlen omistamat osakkeet.

Järkevin syntaksi onkin riippuvainen siitä, mitä ongelmaa kulloinkin ratkaistaan.
Kokeilin tuon netflix.add_holding(netflix) ja ohjelma tekee tämän vaikka ei siinä järkeä olekaan. Eli teknisesti ottaen olioon voi viitata olion omasta muuttujasta käsin, (jos siihen olisi joku järkevä syy), mikä on aika selvää nyt kun ajattelen, että onhan luokissa (melkein aina) tämä self-muuttujakin joka viittaa olioon itseensä. Pitää jatkaa harjoituksia:btooth:. Tässä linkki yhteen mun mielestä aloittelijalle hyvin sopivaan opiskelu-materiaaliin. Yhden tunnin videoiden katsomisen perusteella vaikutti hyvältä.
 
Yritän kirjoittaa testejä Dockerin kanssa Pythonsoftalleni ja ongelmaksi tuli eräs C-kirjasto, jota ohjelmani käyttää.

Softa asentuu setuputilsilla ja C-moduuli on otettu mukaan ext_modulesilla, mikä käsittääkseni on oikea tapa.
Koodi:
MODULES.append(Extension("mosstack.Decoding.raw2fits",
    sources=["mosstack/Decoding/raw2fitsmodule.cpp"],
    libraries=["cfitsio", "raw", "m"], 
    define_macros=[('MAJOR_VERSION', '0'), ('MINOR_VERSION', '8'),],
    extra_compile_args=["-O3"])

Ongelmaksi tulee kumma kyllä erot hostin ja dockerin kirjastojen kanssa. Jostain syystä dockerissa prosessi feilaa siihen, ettei libraw.so.16 löydy. Container on debian stretch ja sieltä löytyy libraw.so.15, mutta 16:sta jostain syystä ohjelma haluaa.

Sen verran onnistuin jäljittämään syitä, että jos containeriin lataan gitistä puhtaan koodin ja teen python setup.py install, niin homma toimii. Jos sen sijaan käytän hostilla muokkaamaani koodia, jonka docker-compose ottaa containerin työkansioon automaattisesti, niin ei toimi. Ilmeisesti hostilla kääntämääni ohjelmaan jää jonnekin maininta tuosta hostin kirjastoversiosta. Build-kansion poistaminen ei auta, enkä grepillä löydä mitään mainintaa libraw.so.16:sta.
 
Viimeksi muokattu:
pythonilla vuoropohjainen lautapeliä, jossa vihollisen liikkeet koitan saada samalle kartalle missä itse pelaajan siirrot. peli näyttää toistaiseksi matopeliltä, jossa vihollinen ei liiku mihinkään ja pelaaja vain liikkuu.

ensimmäinen devaus kysymys.
en onnistu lisäämään vastustajamove() funkkaria itse ohjelmaan,
onko niin että loopin aikana ei voi karata aliohjelmaan?
itse ohjelma
Koodi:
# vuoron aloitus ja  itse peli


while vuoro <= 20:
    suunta = (input("suunta W,A,S,D:? "))
    if suunta == "w":
        x = x - 1
        vuoro = vuoro + 1
    elif suunta == "a":
        y = y - 1
        vuoro = vuoro + 1
    elif suunta == "s":
        x = x + 1
        vuoro = vuoro + 1
    elif suunta == "d":
        y = y + 1
    else:
        print("väärä suunta")
        vuoro = vuoro + 1
    if x < 0 or y < 0 or x > koko1 or y > koko1:
        print("alue ylitetty")
        break

    uusisijainti = board[x][y]
    board[x][y] = pelaaja
    vastustajamove() #miksi  tämä ei toimi tässä?
    vastustajasuunta() #miksi tämä ei toimi tässä?
    print(vuoro)
    print_board(board)

Koodi:
def vastustajamove():
    global valinta
    valinta = random.choice(liiku)
    return valinta
 
Viimeksi muokattu:
pythonilla vuoropohjainen lautapeliä, jossa vihollisen liikkeet koitan saada samalle kartalle missä itse pelaajan siirrot. peli näyttää toistaiseksi matopeliltä, jossa vihollinen ei liiku mihinkään ja pelaaja vain liikkuu.

ensimmäinen devaus kysymys.
en onnistu lisäämään vastustajamove() funkkaria itse ohjelmaan,
onko niin että loopin aikana ei voi karata aliohjelmaan?
itse ohjelma
Koodi:
# vuoron aloitus ja  itse peli


while vuoro <= 20:
    suunta = (input("suunta W,A,S,D:? "))
    if suunta == "w":
        x = x - 1
        vuoro = vuoro + 1
    elif suunta == "a":
        y = y - 1
        vuoro = vuoro + 1
    elif suunta == "s":
        x = x + 1
        vuoro = vuoro + 1
    elif suunta == "d":
        y = y + 1
    else:
        print("väärä suunta")
        vuoro = vuoro + 1
    if x < 0 or y < 0 or x > koko1 or y > koko1:
        print("alue ylitetty")
        break

    uusisijainti = board[x][y]
    board[x][y] = pelaaja
    vastustajamove() #miksi  tämä ei toimi tässä?
    vastustajasuunta() #miksi tämä ei toimi tässä?
    print(vuoro)
    print_board(board)

Koodi:
def vastustajamove():
    global valinta
    valinta = random.choice(liiku)
    return valinta
Pitäisi nähdä koko ohjelma.
 
Koodi:
"""ohjelma luo halutun kokoisen boardin ja käyttäjä liikuttaa pelaajaa boardilla."""
import datetime
import random


date = datetime.datetime.now()
print(date)
nimi = input("kerro pelaajan nimi: ")
print("Heippa "+ nimi +" nyt pelataan!")

board = []
koko = 25
koko1 = koko-1

#piirretään kartta
for x in range(koko):
    board.append(["[ ]"] * koko)

def print_board(board):
    for row in board:
        print(" ".join(row))
print_board(board)

#Aloitus paikan ja pelaajan määrittely
x = 4
y = 4
vuoro = 0
pelaaja = "[@]"
lähtöruutu = board[x][y]
uusisijainti = board[x][y]
board[x][y] = pelaaja
print_board(board)

#vihollisen lähtötiedot
x1 = 10
y1 = 15
enemy = "{Ö}"
liiku =["w","a","s","d"]
valinta = "a"

def vastustajamove():
    global valinta
    valinta = random.choice(liiku)
    return valinta


print("tämä on testi: " + valinta)

def vastustajasuunta():
        global x1
        global y1
        suunta = valinta
        if suunta == "w":
            x1 = x1 - 1   
        elif suunta == "a":
            y1 = y1 - 1   
        elif suunta == "s":
            x1 = x1 + 1
        elif suunta == "d":
            y1 = y1 + 1
        if x1 < 0 or y1 < 0 or x1 > koko1 or y1 > koko1:
            print("alue ylitetty")

vastustajamove()
vastustajasuunta()

uusisijainti2 = board[x1][y1]
board[x1][y1] = enemy

# vuoron aloitus ja  itse peli


while vuoro <= 20:
    suunta = (input("suunta W,A,S,D:? "))
    if suunta == "w":
        x = x - 1
        vuoro = vuoro + 1
    elif suunta == "a":
        y = y - 1
        vuoro = vuoro + 1
    elif suunta == "s":
        x = x + 1
        vuoro = vuoro + 1
    elif suunta == "d":
        y = y + 1
    else:
        print("väärä suunta")
        vuoro = vuoro + 1
    if x < 0 or y < 0 or x > koko1 or y > koko1:
        print("alue ylitetty")
        break

    uusisijainti = board[x][y]
    board[x][y] = pelaaja
    vastustajamove()
    vastustajasuunta()
    print(vuoro)
    print_board(board)
 
Koodi:
"""ohjelma luo halutun kokoisen boardin ja käyttäjä liikuttaa pelaajaa boardilla."""
import datetime
import random


date = datetime.datetime.now()
print(date)
nimi = input("kerro pelaajan nimi: ")
print("Heippa "+ nimi +" nyt pelataan!")

board = []
koko = 25
koko1 = koko-1

#piirretään kartta
for x in range(koko):
    board.append(["[ ]"] * koko)

def print_board(board):
    for row in board:
        print(" ".join(row))
print_board(board)

#Aloitus paikan ja pelaajan määrittely
x = 4
y = 4
vuoro = 0
pelaaja = "[@]"
lähtöruutu = board[x][y]
uusisijainti = board[x][y]
board[x][y] = pelaaja
print_board(board)

#vihollisen lähtötiedot
x1 = 10
y1 = 15
enemy = "{Ö}"
liiku =["w","a","s","d"]
valinta = "a"

def vastustajamove():
    global valinta
    valinta = random.choice(liiku)
    return valinta


print("tämä on testi: " + valinta)

def vastustajasuunta():
        global x1
        global y1
        suunta = valinta
        if suunta == "w":
            x1 = x1 - 1 
        elif suunta == "a":
            y1 = y1 - 1 
        elif suunta == "s":
            x1 = x1 + 1
        elif suunta == "d":
            y1 = y1 + 1
        if x1 < 0 or y1 < 0 or x1 > koko1 or y1 > koko1:
            print("alue ylitetty")

vastustajamove()
vastustajasuunta()

uusisijainti2 = board[x1][y1]
board[x1][y1] = enemy

# vuoron aloitus ja  itse peli


while vuoro <= 20:
    suunta = (input("suunta W,A,S,D:? "))
    if suunta == "w":
        x = x - 1
        vuoro = vuoro + 1
    elif suunta == "a":
        y = y - 1
        vuoro = vuoro + 1
    elif suunta == "s":
        x = x + 1
        vuoro = vuoro + 1
    elif suunta == "d":
        y = y + 1
    else:
        print("väärä suunta")
        vuoro = vuoro + 1
    if x < 0 or y < 0 or x > koko1 or y > koko1:
        print("alue ylitetty")
        break

    uusisijainti = board[x][y]
    board[x][y] = pelaaja
    vastustajamove()
    vastustajasuunta()
    print(vuoro)
    print_board(board)
Et päivitä vastustajan sijaintia kartalle liikuttamisen jälkeen. Eli täytyy lisätä board[x1][y1] = enemy vastustajasuunta()-funktioon. Toki tuo ei pelkästään riitä, koska vanhatkin sijainnit jäävät muistiin, mutta perusongelma on kuitenkin tuo.

Sellainenkin bugi tuossa on, että vuoroa ei lisätä alas liikkuessa, mutta väärin liikkuessa lisätään. Tässä olisi hyvä tilaisuus purra luotia ja opetella hiukan olio-ohjelmointia niin että teet luokan pelaajalle, boardille ja vastustajalle.

Olisi myös suotavaa ottaa talteen vastustajamove() palauttama arvo, ja antaa se parametrinä vastustajasuunta() -funktiolle. Ylipäänsä jos kirjoittaa kerrankin global niin se on merkki siitä että jotain voisi tehdä paremmin.
 
Kiitos paljon, viesti auttoi ajattelemaan tarkemmin ja luin yhden dokkarin olio ohjelmoinnista
alla esimerkki koodia miten lähdin korjaamaan. tarjosin muuttujia funktioille.
nyt sain vihollisen liikkumaan randomisti. molemmat jättävät jälkensä vielä. Jälkien poistaminen on seuraavana työn alla.

Koodi:
#vihollisen liike
valinta1= vastustajamove()
x1,y1 = vastustajasuunta(valinta1)
uusisijainti1 = board[x1][y1]
board[x1][y1] = enemy
*versio tallenna commit, sync push ja nukkumaan.
 
Viimeksi muokattu:
Olio-ohjelmointi on tosiaan ihan hyvä tapa ratkaista tällaiset ongelmat, joissa on paljon tilanhallintaa. Mutta onnistuu tuo myös ilmankin, jos oliot eivät ole vielä tuttuja ja/tai haluaa niitä syystä tai toisesta vältellä. En tuota koko ongelmaa viitsinyt lähteä koodaamaan, mutta alla esimerkki, miltä koodi voisi näyttää ilman olioita.

Ja kuten @Karhukainen totesi, missään nimessä ei kannata käyttää globaaleja muuttujia, ja muutoinkin funktioiden ulkopuolella kannattaa ainoastaan määritellä vakioita (kuten MOVEMENT_OPTIONS).

Koodi:
MOVEMENT_OPTIONS = set("w a s d".split())


def create_board(koko):
    board = []
    for x in range(koko):
        board.append(["[ ]"] * koko)
    return board

def print_board(board):
    for row in board:
        print(" ".join(row))

def add_player(board, tag, x, y):
    board[x][y] = tag


def vastustajamove():
    valinta = random.choice(MOVEMENT_OPTIONS)
    return valinta

def main():
    # muuttujat esitellään tässä, jolloin ne eivät ole globaaleja!
    board = create_board(koko=25)
    # ...

if __name__ == "__main__":
    # Kannattaa heti alkuun ottaa tavaksi, että ohjelma käynnistetään tällaisen maagisen if-name-main ehdon sisältä. 
    # Muutoin koodi ajetaan aina importin yhteydessä (mukaanlukien testit yms).
    main()
 

Statistiikka

Viestiketjuista
257 779
Viestejä
4 478 081
Jäsenet
74 011
Uusin jäsen
velle

Hinta.fi

Back
Ylös Bottom