poniedziałek, 8 maja 2017

[DSP2017] 21# Romans z Pythonem cz.3 (moduły, wyjątki, obiekty iterowane, klasy, dziedziczenie, pliki i zarządzanie zasobami)

Pomimo, że rzeczywistość rozszerzona w HoloLens jest zasadniczym tematem dla DSP 2017, o tyle pewien romans z Pythonem również pomału się rozwija. Dziś podzielę się notatkami z kolejnych obszarów Pythona, jakie powstały podczas ich poznawania i praktycznego testowania w Visual Studio 2017 15.2. Nie są to oczywiście wszystkie tajemnice, jakie skrywa ten język, ale powinno wystarczyć to do napisania jakiegoś prostego skryptu. Przedstawiłem tu także klasy i dziedziczenie, które przydadzą się raczej w czymś większym, ale warto być świadomym jak to wygląda.  Trochę to przekombinowali i są pułapki tam, gdzie normalnie ich nie ma.

python_logoball-python

7# Moduły

Jeśli w pliku modułu będzie kod wykonywalny,

image

to wykona się podczas importu:

image

Po zdefiniowaniu funkcji w module:

image

taka sytuacja nie ma miejsca, a funkcję można wywołać albo po imporcie całego modułu:

image

albo po imporcie samej funkcji z modułu:

image

Podobnie jest z importem funkcji statycznych w C# 6.

Możemy też zaimportować wszystkie elementy z modułu pisząc *.

Import relatywny - jeśli importujemy moduł zawarty w bieżącym module (pomijamy nazwę głównego modułu i zaczynamy nazwę od . w tym samym folderze, dwie kropki stosujemy przy odwołaniu do rodzica)

Moduł może się nazywać jak plik lub __main__ (jeśli jesteśmy w skrypcie uruchomionym przez python.exe). Do odczytu służy specjalna zmienna __name__ . Ten sam plik może pełnić funkcję modułu lub skryptu (wywołanie funkcji w zależności czy __name__ == __main__). Może być też zdefiniowana funkcja main. Argumenty przekazywane do skryptu są dostępne jako sys.argv[i]. Istnieje także standardowa bliblioteka argparse.

Dokumentowanie funkcji - pod linijką z definicją opis umieszczamy w potrójnym cudzysłowie, możliwe sekcje Args  i  Returns.

image

Dokumentowanie modułu - na początku pliku, możliwa sekcja Usage.

Komentarz przy linii:

image

Jeśli skrypt rozpoczyna się od linii:

image

to może być wykonany w konsoli za pomocą Python 2 lub 3 przez wpisanie swojej nazwy z rozszerzeniem .py.  Działa to także na Windows.

Pakiet - moduł, który zawiera inne moduły (i pakiety).

Moduły to zwykle pliki, a pakiety to zwykle katalogi. Katalog powinien być w sys.path.

Plik inicjujący dla pakietu: __init__.py

Namespace packages są podzielone na kilka folderów, nie zawierają pliku __init__.py

 

#8 Wyjątki

Coś ala try catch, tylko try except.

image

Nie łapiemy zwykle błędów programowania tj. IndentationError, SyntaxError i NameError. Unikamy obsługi błędów TypeError.

Blok except może nie mieć podanej nazwy błędu.

W bloku except można użyć słowa “pass” (akceptuj i nic nie rób).

Wyrażenie except można zakończyć frazą “as zmienna”.

Wyjątek można ponownie wyrzucić z bloku except za pomocą słowa “raise”.

Istnieje też blok finally.

Try except można stosować też na poziomie modułu (alternatywny import pakietów i inne definicje funkcji).

Można tworzyć własne klasy wyjątków dziedziczące po Exception.

Wyrzucanie wyjątku - raise {obiekt wyjątku}

 

#9 Obiekty iterowane

Szybkie stworzenie nowej listy: [ expr(item) for item in iterable ]

image

{} zamiast [] jeśli chcemy zbiór unikalnych wartości

opcjonalne filtrowanie: [ expr(item) for item in iterable if predicate(item) ]

Szybkie tworzenie słownika: { key_expr:value_expr for item in iterable }

protokoły Iterable i Iterator:

iterator = iter(iterable)

item = next(iterator)

Generatory

image

można używać w pętli for

wyrażenie generatora: ( expr(item) for item in iterable )

można to powielić:

image

szybka suma:

image

itertools - funkcje dla obiektów iterable

wbudowane np. sum, min, max, map, filter, any, all, zip

filtrowanie:

image

moduł itertools np. count, islice

image

moduł functools np. reduce

image

 

10# Klasy

Definicja minimalistyczna:

image

image

W metodach instancyjnych zawsze pierwszym parametrem jest obiekt self.

image

image

Można też zrobić wywołanie na klasie:

image

Klasa może mieć inicjalizator, nie konstruktor:

image

image

Powyższe linijki nie chciały mi się długo wykonać w VS 2017 15.2 Preview. Cały czas jakby nie była odświeżona definicja klasy o inicjalizator z parametrem name. Pomogło dopiero ponowne uruchomienie Visual Studio. Pamiętajmy jednak że to właśnie Preview, a nie wersja finalna.

W Python wszystkie składowe klas są publiczne.

W inicjalizatorze można wyrzucać wyjątki.

W wywołaniu metody można jawnie podać nazwy parametrów.

Atrybut klasowy (nie instancji klasy):

image

image

Metoda statyczna:

image

Zamiast niej można użyć metody klasowej:

image

Metody klasowe mogą służyć jako nazwane konstruktory. Do klasy A można dodać:

image

co pozwala na:

image

Polimorfizm – wystarczy by klasy miały te same składowe.

Dziedziczenie przydaje się głównie do współdzielenia implementacji.

image

Item - klasa bazowa

Klasa abstrakcyjna - nie ma zdefiniowanych wszystkich używanych przez siebie składowych, może być utworzona, najwyżej gdzieś wystąpi błąd.

image

Więcej współdzielenia kodu:

image

Metody statyczne w klasach pochodnych mogą być nadpisywane (po prostu są jeszcze raz definiowane).

Jeśli klasa pochodna nie ma inicjalizatora, to zostanie wywołany z klasy bazowej.

W inicjalizatorze klasy pochodnej możemy wywołać inicjalizator klasy bazowej (nie dzieje się to automatycznie). Klasa B dziedzicząca po wyżej przedstawionej klasie A może wyglądać następująco:

image

Stworzenie przez inicjalizator zadziała. Błąd wystąpi, gdy będziemy chcieli skorzystać z metody create_empty:

image

Wtedy w klasie bazowej A trzeba zmodyfikować nieco definicję create_empty:

image

image

Propercje:

W klasie B _system można opakować w getter i setter:

image

W przypadku settera nazwa atrybutu zaczyna się od nazwy gettera. Visual Studio 2017 Preview 15.2 nie podświetliło go jeszcze.

image

Przy dziedziczeniu getterów i setterów trzeba uważać. Przy definicji:

image

wszystko nam zadziała:

image

ale  gdyby był sam setter w klasie C, to trzeba by zmienić atrybut nad nim na:

image

Z kolei, gdy zostawimy taki atrybut nad setterem w klasie C, a będzie tam getter taki jak na początku, to zamiast niego będzie wywoływał się ten z klasy bazowej!

Problem z wywołaniem settera klasy bazowej w setterze klasy pochodnej.

image

image

Pomoże tutaj jawne wywołanie funkcji settera z klasy bazowej:

image

Sprawdzanie czy obiekt jest danej klasy:

image

Sprawdzenie czy dana klasa dziedziczy z innej klasy:

image

Każda klasa dziedziczy po wbudowanym typie object.

W Pythonie można dziedziczyć po wielu klasach równocześnie

  • Jeśli klasa pochodna nie ma inicjalizatora, to wywoływany jest z pierwszej klasy bazowej
  • składowa __bases__ – krotka klas bazowych
  • składowa __mro__ lub mro() – “method resolution order”, szukana jest pasująca metoda po kolei w klasach według MRO, brana jest pierwsza znaleziona, MRO zależy od kolejności podania klas bazowych w definicji klasy, MRO wylicza Python w oparciu o algorytm C3
  • super() zwraca rozwiązania metod z klas za daną klasą w MRO

 

11# Pliki i zarządzanie zasobami

Pliki - odczyt i zapis w trybie binarnym i tekstowym, różne tryby np. czyszczenie przed zapisem lub dopisywanie na końcu itp.

Przykładowy zapis do pliku tekstowego:

image

Można też jednorazowo zapisać listę linii za pomocą funkcji writelines().

Odczyt:

image

Korzystanie z pliku kończymy tak jak poprzednio wywołaniem funkcji close. Można odczytywać daną liczbę znaków (np. read(5)), cały plik (read()), linię (readline()) lub wszystkie linie w formie listy (readlines()).

Zamiast print() można użyć sys.stdout.write() z modułu sys. Pozwala to uniknąć podwójnych odstępów między liniami, jeśli linia kończyła się na ‘\n’:

image

Funkcja open() zwraca menadżera kontekstu. Powyższy kod lepiej zapisać w bloku with, który zamyka plik także po wystąpieniu błędu podczas odczytu (ręcznie do obsługi tego trzeba by użyć bloku try finally).

image

image

Inne obiekty podobne do plików:

image

Można też samemu zdefiniować menadżera kontekstu, ale nie będziemy się teraz w to wgłębiać.

 

Myślę, że starczy tego Pythona na dziś. Następnym razem, jeśli będę coś o nim pisał, pójdziemy w jakiś przykład praktycznego zastosowania.

środa, 3 maja 2017

[DSP2017] 20# Holowizja - przyszli po mnie do pokoju, ale ich zabijam

Nasza wersja gry SurvivalShooter na HoloLens zaczyna pomału nabierać kształtów. Tym razem dorzuciłem pozostałe rodzaje wrogów - ZomBear oraz Hellephant, a także dorobiłem prosty licznik punktów.

A więc przyszli po mnie…

HZX2

Ale ja: pif-paf, pif-paf, pif-paf…  Na początku zabiłem największego zombi za 50 punktów! Los kolejnych jest już policzony…

HZX3

Od strony Unity w Assets w prefabs przybyły odpowiednie prefabrykaty wrogów. Na scenie póki co statycznie umieściłem dwa dodatkowe zombi oraz Canvas z polem tekstowym do wyświetlania punktów. W stosunku do oryginału licznik jest skromniejszy, nie chciałbym by zbytnio absorbował uwagę, może i tak trochę przeszkadzać, ale na pierwszy raz ujdzie.

HZX4

Pole tekstowe zaopatrzone jest w znany już skrypt ScoreManager. Aby punkty się dodawały odkomentowałem odwołanie do niego w skrypcie EnemyHealth.

Aktualne źródła znajdują się jak w poprzednio w HoloSurvivalShooter na githubie. Dwa punkty “to do” z wymienionych w poniedziałek zrobione.  Te były dość proste, przed nami szykują się trudniejsze. Zombi, aby stać się groźne, muszą same się przemieszczać i to tak, by posiadacza HoloLens na głowie osaczać.  Do następnego spotkania z zombi!

poniedziałek, 1 maja 2017

[DSP2017] 19# Romans z Pythonem cz.2 (Visual Studio 2017 Preview 15.2 & SQL Server 2017 CTP 2.0)

Dziś sobie zrobimy kontynuację mini cyklu związanego z Pythonem. Ostatnio pokazałem częściowo tajniki składni tego języka. Tych tajników jest znacznie więcej i napiszę o nich jeszcze, tym razem jednak podzielę się informacjami na temat wsparcia Pythona w najnowszych wersjach narzędzi Microsoft.

Zacznijmy od Visual Studio 2017. O ile przed wersją finalną były dostępne moduły instalacyjne dla Pythona i Data Science, o tyle nie trafiły ostatecznie do wersji finalnej. Update 1 czyli wersja 15.1 też tego nie przynosi. Dopiero rozwojowa wersja 15.2 znowu oferuje te funkcjonalności. Możemy ją sobie zainstalować bez żadnych komplikacji obok stabilnej wersji produkcyjnej. Widzimy, że z poziomu Visual Studio możemy wskazać instalację Pythona, a także środowiska Anaconda.

vs2017_preview_install

W nowym Visual Studio możemy tworzyć projekty w języku Python dedykowane dla machine learning, a także Web. Machine learning w Pythonie to też dobry temat na niejednego posta, dziś jednak skupiam się na samych narzędziach. W projekcie dla machine learning usunąłem z pliku .py predefiniowany kod i napisałem w nim kilka prostych linijek. Zauważmy, że mam Intellisense. Przy czym od razu mi nie zadziałał. Wchodząc w ustawienia edytora wg. wątku Visual Studio 2017 Preview with Python (Intellisense not working) odznaczyłem opcję “Hide advanced members”.

vs_python_1b

Mamy też okno interaktywne dla Pythona. Dodam że edytor obsługuje podział kodu na komórki w takim samym formacie, w jakim zapisuje sesję Jupyter. Rolę separatora pełni znacznik #%%. Jeśli naciśniemy CTRL + Enter przy kursorze w obrębie danej komórki, to jej kod zostanie przeklejony do okna interaktywnego i wykonany.

vs_python_2b

Visual Studio zapewnia też wygodne debugowanie.

vs_python3b

Aktualności dotyczące Python Tools dla Visual Studio można śledzić na oficjalnym blogu Python Engineering at Microsoft. Jeśli z jakichś powodów nie możemy korzystać z najnowszej wersji 2017, to nie wszystko stracone. Istnieją Python Tools dla wcześniejszych wersji i to nie od wczoraj. Mogę polecić jeszcze cykl mini-szkoleń Getting Started with Python Development using Visual Studio. Co prawda jest z 2015 roku, ale pokazane w nim funkcjonalności są nadal aktualne w 2017. 

Przejdźmy teraz do najnowszego SQL Server 2017. Wszystko zaczęło się od konferencji Data Amp, a esencją jest prezentacja Python based machine learning in SQL Server, której obejrzenie szczerze polecam. Warto odwiedzać też co jakiś czas SQL Server Blog, a zwłaszcza przeczytać umieszczony na nim post Python in SQL Server 2017: enhanced in-database machine learning. Jak szybko zacząć cieszyć się Pythonem w SQL Server? Linki do pobrania ostatniej wersji CTP 2.0 zebrane są w oficjalnym poście SQL Server 2017 Community Technology Preview 2.0 now available. Rejestrujemy się i wybieramy platformę. Póki co wybrałem instalkę dla Windows. Podczas instalacji zaznaczyłem komponenty związane z Pythonem i machine learning.

sql_1

Następnie zainstalowałem najnowsze SQL Server Management Studio dostępne w wersji 17.0, które obsługuje już SQL Server 2017. Po czym odblokowałem uruchamianie zewnętrznych skryptów:

sp_configure 'external scripts enabled', 1; 
RECONFIGURE;

Zrestartowałem serwer. Następnie uruchomiłem sobie skrypt jak poniżej:

sql_2

Przyznaję, 17s pomieliło (narzut pomiędzy T-SQL a Pythonem? wczesna wersja CTP?), po czym wyskoczyły efekty wykonania. Generalnie bardzo fajna sprawa, zastanawia mnie tylko nieco długi czas wykonywania w stosunku do samego Pythona.

W następnym odcinku o Pythonie powrócimy do rozważań nad samym językiem.

[DSP2017] 18# Holowizja - zabiłem pierwsze zombi w pokoju

Ten wpis miał pojawić się w niedzielę, ale z uwagi na liczne problemy techniczne pojawia się z lekkim opóźnieniem teraz. Postanowiłem opracować wersję gry Survival Shooter na HoloLens.

Ostatnio w pierwszym kroku umieściłem jednego z wrogów o nazwie ZomBunny w pokoju.

HZ1

Można do niego strzelać (tap click, póki co bez błysku światła i promienia). Jeśli strzelimy i będzie przez nas namierzony kursorem to straci część swojego życia. Po kilku celnych strzałach padnie na wznak i zniknie.

Aktualne źródła umieściłem w HoloSurvivalShooter na github. A jak to wygląda od strony Unity?

Bazując na projekcie HoloVision9 usunąłem z niego sześciany oraz skrypt do rzucania piłką z kamery. Zamiast tego dodałem na scenę Zombunny bez NavMeshAgent’a i na razie z jednym skryptym EnemyHealth z zakomentowanymi odniesieniami do tego agenta i do ScoreManager.

image

Na kamerze umieściłem AudioSource z dźwiękiem wystrzału oraz uproszczony skrypt PlayerShooting, tym razem łapiący “kliknięcie” użytkownika poprzez SetGlobalListener.

HZ3

Kod tego skryptu aktualnie wygląda tak:

public class PlayerShooting : MonoBehaviour, IInputClickHandler
{
    public int damagePerShot = 20;
    public float timeBetweenBullets = 0.15f;
    public float range = 100f; 


    float timer;
    Ray shootRay = new Ray();
    RaycastHit shootHit;
    int shootableMask;
    AudioSource gunAudio;
    float effectsDisplayTime = 0.2f;


    void Awake ()
    {
        shootableMask = LayerMask.GetMask ("Shootable");
        gunAudio = GetComponent<AudioSource> ();
    }


    void Update ()
    {
        timer += Time.deltaTime;

        if(timer >= timeBetweenBullets * effectsDisplayTime)
        {
            DisableEffects ();
        }
    }


    public void DisableEffects ()
    {
        //to do         
    }

    public void OnInputClicked(InputEventData eventData)
    {
        if(timer >= timeBetweenBullets && Time.timeScale != 0)
        {
            Shoot ();
        }
    }


    void Shoot ()
    {
        timer = 0f;

        gunAudio.Play ();

        shootRay.origin = transform.position;
        shootRay.direction = transform.forward;


        if(Physics.Raycast (shootRay, out shootHit, range, shootableMask))
        {
            EnemyHealth enemyHealth = shootHit.collider.GetComponent <EnemyHealth> ();
            if(enemyHealth != null)
            {
                enemyHealth.TakeDamage (damagePerShot, shootHit.point);
            }            
        } 
    }
}

Jakie dalsze kroki?

  1. Wiele rodzajów zombi
  2. Tablica zdobytych punktów
  3. Poruszanie się zombi po podłodze pokoju w kierunku gracza (kamery)
  4. Atak zombi i utrata życia przez gracza
  5. Informacja o stanie życia grającego i o końcu gry
  6. Dynamiczne dodawanie zombi na scenę
  7. Ogień, światło i promień z naszej lufy (kamery lub dłoni)
  8. Muzyka w tle

Jak widzimy trochę tego jest, więc będzie co dłubać w maju. Początkowo wydawało się, że przeniesienie ze zwykłego Unity będzie znacznie szybsze, ale natrafiłem na kilka problemów.

Po instalacji Visual Studio 2017 lub updacie Windows 10 w edycji Insider przestał działać mi emulator HoloLens. Znany błąd o braku uprawnień w ACL, czytałem, potem doszedłem do problemu z wgrywaniem i… skończyło się na instalacji czystej wersji Windows 10 oraz Visual Studio 2017 i Unity. Plusem całej sytuacji jest to, że działam na Visual Studio 2017 zgodnie z opisem na stronie o narzędziach dla HoloLens. Przejściowym minusem jest nieco mniejszy stopień integracji z Unity (do edycji skryptów uruchamia mi się Mono Develop). Nie chciałem też iść w najnowsze Unity 5.6 z obawy o kompatybilność i wybrałem wersję 5.5.3.

Inny problem to czarny ekran w emulatorze. Jak próbowałem dodawać dynamicznie wroga na scenie, to ani razu mi się siatka pokoju nie wyświetliła. Wróciłem nawet do ostatnio używanej wersji Unity 5.5.2, ale nic to nie zmieniło. Niezależnie od tego losowo czasami i tak ta siatka może się nie pojawić, ale tak było u mnie zawsze (może trzeba stosować jeszcze starsze Unity jak to ostatnio znane na stronie z narzędziami?). W porównaniu do zwykłego Unity testowanie związane z eksportem do Visual Studio i odpalaniem przez emulator jest dużo bardziej uciążliwe niż odpalenie w ekranie Unity.

Kolejna rzecz, która mnie spowolniła to ogień z lufy. Myślałem że przeniesienie będzie proste, ale jakoś ten błysk świetlny widoczny nawet w edytorze nie wyświetlał się w emulatorze, a promień czy światło w sumie też nie pozwalały się zauważyć (sporadycznie kawałek promienia zostawał gdzieś na siatce pokoju). Na razie to wyrzuciłem i bedę nad tym myślał.

Pewną zagadką do rozwiązania będzie też przemieszczanie się po podłodze w kierunku grającego. Tutaj siatka pokoju jest dodawana dynamicznie, a NavMeshAgent wymaga w trakcie edycji stworzenia płaszczyzny do poruszania. Z tego, co rzuciłem okiem, nie wiadomo, czy go się w ogóle użyje.

Tablicę z punktami, pasek życia czy tym bardziej niebieski ekran game over też się nie przeniesie bez zmian. Gdzieś wyczytałem, że w HoloLens obiekty stale zakrywające pole widzenia użytkownika nie są zalecane. Do przemyślenia.

Do zobaczenia następnym razem. A nastąpić musi on stosunkowo niedługo z uwagi na dzisiejszą małą obsuwę.