2020年3月31日火曜日

python基本規格測試

 之前寫的python規格測試用的東西。可以用jupyter丟進去觀察結果。


#!/usr/bin/env python3

# -*- coding: utf8 -*-
# 除了迴圈/if判斷/函式定義/class定義需要冒號":"之外,行尾不需終結符號
# 註解建議使用linux script style : 井號後面加上空格再開始寫
# 可以跳行的長字串: 使用三個雙引號定義(""")
#     第一個沒有被指定變數的長字串會被當成 class的 docstring (可用變數名稱 __doc__ 存取)
# 靠縮排4格來定義下一層的指令(函數,迴圈,判斷式的後續動作等等)

import sys # for console arguments

""" 這是長字串
第二行 """

longStr2 = """ 這是長字串2
 2的第二行 """

print("arg0=" + sys.argv[0])
print(longStr2)
print(__doc__)

__doc__ =""" 這是長字串3
3的第二行 """
print(__doc__)


print('unicode -------------------------------------')
strTmp= '一二三四' # 變數不需宣告,也會持續存在。重複使用時的初始值必須考慮。可以用del指令刪除變數。

# 注意,一個變數被assign初始之後,不要再assign不同的資料結構,容易出現不明問題。

i = -1
j = 6
print(strTmp[i:j])  # 多碼字不是以byte計算,以一個文字為單位。
print(strTmp[1])


# 使用陣列index做存取,在超過陣列範圍的情況下,會產生outbound index exception, 但是套用array slice則不會。
#    slice可以為負值,不管起終點都是指從陣列尾倒數。
# 字串物件其中的字不可被修改,只能經過處理產生新的



# 序列資料結構: list  [obj1, obj2, ...] ,可以單獨置換list元素,可以slice指定置換list元素,可以接合兩個list
# slice的置換可以只給1個元素"z",也可以丟list["y","z"],起點跟終點相同的話,視同插入。

print('list -------------------------------------')
arJ =  [3,4]
arI = [0,1,2] + arJ
print(arI)
print(len(arI))
arI[0:0] = "x"  #在陣列頭插入
print(arI)
arI[0:1] = "y"  #在陣列頭置換
print(arI)
arI[2:4] = ["a", "b"]  #在陣列中置換。
print(arI)
arI[2:3] = ["y", "z"]  #在陣列中置換。注意:若是slice的起終點的差,小於置入陣列的大小,視為插入。
print(arI)
arI[0:1] = arI  #在陣列頭插入自己,結果是會重複內容
print(arI)


# 多層list, 不可使用slice方式置入
arI[2] = arJ
print(arI)


# 迴圈, 條件表示式,需在尾巴加上":"

print('while loop -------------------------------------')
i = 0
j = 0
while (i < 10):
    j = j + i
    i = i + 1


print(j)

pass # pass: 不做任何事情。 在一定要有命令行的地方可以發揮作用。



# 序列資料結構 touple: 小括號"()"表示。只有一層可以不寫小括號,直接用逗號定義內容,
#     但是第二層之後必須有括號(obj1, int2, ...) ,應該是故意要跟function call相同
# 建立之後不可改變內容,但是裡面的元素可以改變內容。性質與list類似。元素內容有順序,可重複元素。
# 可使用index存取: touple[0]
# 建立0個元素的內容: 固定指令()
# 建立只有一個元素的內容,必須在後面加上逗號。 (obj, ) 原因猜測是會跟function call parameter重複定義



print('touple -------------------------------------')
x = "x"
y = "y"
z = "z"

touple = (x, y, z)  # touple packing
a, b, c = touple  # 可以拆解。 注意變數數量必須跟touple的元素數量相同, 太多或太少都會出ValueError
print(touple)
print(touple[0])
print(a + b + c)



print('set -------------------------------------')
# 序列資料結構 set: 以大括號"{}"表示。 而且跟list一樣必須寫。
# 元素順序由系統自行決定不可自訂(操作前是固定的,操作後不保證會不會變動)
# 元素內容可變,但不可重複。放入重複元素會以後蓋前的方式運作。
# 無法循序存取,只能遍歷。有基本的add / remove / pop / clear操作
# 建立0個元素的內容: 固定指令set(),不可使用{},會建立另一種資料結構:dict。
# 無刪除元素指令

a = set('abracadabra')  # 注意: 以set()初始化字串的話,結果會是一個單字串的set
b = set('alacazam')
print(a)
print(b)



# 序列資料結構 dictionary : 同樣以大括號"{}"表示,但是定義內容以key: value的方式定義。 運作方式類似java的map, objective c也有dictionary。
# key必須為不可變的物件,像是字串,數字,boolean。也可以用touple定義,但是此時的touple不可包含可變元素。
# 元素有順序,內容可變,要列舉可以用list(dic)取得key list,也可以用dict.items()取得key-value iterable
# 無法直接取得value list。使用 in 運算子只能比對key。
# key: value必須同時存在,key值不會重複,跟set一樣,設定重複的值會以後蓋前的方式運作。
# 比對兩個dict相同,不會管key值順序。



print('dict -------------------------------------')
tel = {'jack': 4098, 'sape': 4139, 'sape': 4140}
print(tel)
tel['guido'] = 4127  # 可使用list運算存取。
print(tel)
del tel['sape'] # 可以用del指令刪除key(value也會消失)。刪除不存在的key會出現KeyError。
print(tel)


tel = dict([('sape', 4139), ('guido', 4127), ('jack', 4098)]) # 可以靠touple定義key: value值
print(tel)
tel2 = dict(zip(['one', 'two', 'three'], [1, 2, 3])) # zip(): 把兩個同元素數量的序列以順序合併成touple組合
print(tel2)






# for : 只能對陣列型物件操作。 要寫一般的數字跑法的話,可以使用range(起始, 終點, step)產生iterable物件。
# 或是使用此法產生數字陣列: list(range(起始, 終點, step))
# for的list操作,建議使用slice功能產生copy,避免即時操作list導致iterate成為無窮迴圈
# for/while else : 疊代跑完沒被break的話就執行。若是要設計find流程好用。
# 其他技巧: 可以用dict.items()取得key-value iterable
# 可以用enumerate()取得index-value touple(iterable)
# 二階迴圈可以靠zip()實作

print('for loop -------------------------------------')
i = 0
for ar in range(10): # 只會跑0 ~ n-1的部份
    i = i + ar

print(i)
print(ar)  # 回傳值為9 (也就是n-1)。 參數會持續存在。



for f in enumerate(a):  # 印出來會是touple組合。
    print('enumerate {0}= {1}'.format(f[0], f[1]))


index = [0, 1, 2]
value = ['a', 'b', 'c']

for i, v in zip(index, value):  # 二階迴圈可以靠zip()實作
    print('index[{0}]= {1}'.format(i, v))

   

   

   

# function : 不可控制外部變數,除非以global做特別宣告。 參數加上預設值代表option參數(caller給不足參數的話就會使用預設值)
# 沒給預設值的參數,caller必須給值,要不然執行會錯誤。可以不照參數順序給,但是要使用<parameter name>=value的方式指定
# 使用<parameter name>=value的方式指定,不可重複給同一個參數值
# option參數也可以不照順序給,同樣使用<parameter name>=value的方式指定
# option參數的預設值假如是物件的話,操作過的結果會被保留。若是有重複call的動作要注意。解決方案如下面的程式碼的參數z。
# 函式的參數可以帶函式(因為函式本身就是一個物件)。
# 函式物件的物件定義: function.__self__ --- 一個帶著函式方法fucntion()的物件。
# 函式物件的函式定義: function.__func__(self, param1, param2, ...) --- 等同於class method。
# 參數名帶*號,會收集傳進來的所有照順序給的參數(除了已指定的之外)。
# 參數名帶**號,會收集傳進來的所有<parameter name>=value的方式指定的參數(除了已指定的之外)。
# 傳入順序參數時,可以利用list傳入。 但是要在參數名前面加上*號
# lambda function: 單行的無名function可以不需寫def來定義。參數可以參照上層。
print('function -------------------------------------')

def plusplus(n, x=1, y= ["zzzzz"], z = None):
    if z is None:
        z=[]

    n = n + 1  # python沒有累加算符(++)
    x= x + 1
    print(x)
    y.append("a")   # 操作的結果會被保留
    print(y)
    return n        # 不指定回傳值(包含只寫了return)的話,caller會接到None這個物件。


print(plusplus(i))
print(plusplus(i))
print(plusplus(*[i, 2]))
print(plusplus(i, y=['yyyyy'])) # lambda function
print(i)


def funcplus(a,b):
    return a(b)



print('function parameter -------------------------------------')
print(funcplus(plusplus, 1))


# 判斷式if : 可以用 in (datatype1, objet2, list3, ...) 的方式比對一個個obj。這種touple表示的物件,因為只比對第一層的obj表列,並不會比對陣列內部內容
print('if else elif -------------------------------------')
if (arI) in (45, arI):  # in也是比較運算子。其他的比較運算子: == && < > <= >=
    print("here")
else:
    print("there")

if ([4, 3]) in arI[:]:  # in直接丟一個list物件進去的場合,不同的list, 只要順序跟內容相同,就會被找到
    print("revarray")

if (arJ) in arI[:]:  # 不只是同一個list,不同的list, 只要順序跟內容相同,也會被找到
    print("array")

if ([3, 4]) in arI[:]:  # 不只是同一個list,不同的list, 只要順序跟內容相同,也會被找到
    print("newarray")

print(arI in (45, arI)) # 簡易比較的寫法。其他語言也有



# 資料型態: None (一般語言的null,注意大小寫,是系統預設的物件)
# NaN : 非數字,是系統預設的物件
# boolean : True / False (注意大小寫)
# assign 的動作只能出現在左端,不能出現在比較式內。
# None的比對敘述: if a is None:


# 模組: import 之後會跑一次模組檔案的內容,也就是所謂的初始動作。
# as : 別名
# 模組內參數存取:modname.itemname
# 模組只會初始化一次,而且永久有效。所以要是import過之後改了模組,在不重啟python執行器的狀況下要更新模組,
# 可以 import importlib,再執行 importlib.reload(modulename) 。

# 如何讓模組判斷現在是當成主程式在跑:  if __name__ == "__main__"   這樣方便單獨測試。

# 模組路徑: 1. 主模組執行的同一個目錄 2. PYTHONPATH
# 如何增加模組搜尋路徑: sys.path.append('<路徑>')

print('import -------------------------------------')

import requests as http

print(dir())  # 這樣可以印出目前有用到的所有method跟變數。



headers = {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
    "Accept-Language": "zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4",
    "Accept-Encoding": "gzip, deflate",
    "User-Agent": "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.89 Safari/537.36",
}

url = "https://udn.com/news/story/7338/4091931"



# print(sys.path)
# try:

# r = http.get(
#    url, verify=False, headers=headers, timeout=(10, 30)
#    )  # ,proxies=proxies)
# except:
# print(r.text)


# 例外處理 : try / except 一個try可以有多個except, 若是except沒指定,會傳到上層的try / except。 都沒捕捉的話就拋出系統錯誤,停止執行
# except可以一次捕捉多個。  格式: except (RuntimeError, TypeError, NameError):
# raise : 丟出例外
# 在例外處理中再丟出例外,就是給上一層處理。
print('try except -------------------------------------')




print('class -------------------------------------')
# class定義方式: class ClassName(Extend from class1, Extend from class2, ...):
# self : 物件本身 (就是java/c++的this)
# class的初始化: 定義函數 __init__(self, arg0, arg1,...)
# new一個class(產生一個class物件): ClassName(arg0, arg1,...)
# 物件函式與class函式的關係: 假設x為MyClass的物件, x.f() 等同於 MyClass.f(x) 。python不分class method或是object method...
# class內定義參數的話,所有從同一個class產生或是繼承的物件共享該參數。
# class method要操作物件的參數,必須以self操作。
# object.__class__ : 判斷該object是哪種class(就是傳回該物件的class定義結構)。
#    預設的class比對方法: isinstance(obj, <classname>)
#    是不是繼承class的比對: issubclass(object.__class__, <classname>)

# class內部參數: __name__ ,會給class的可讀名稱
# 定義一般函式,要給class/object 當作method用的話,第一個參數必須是self。

# 繼承可以指定模組內的某個class。
# 繼承來的class function可以改寫(因為沒有特別的阻擋機制)
# class function改寫後要call baseclass的函式也是可以的。就直接執行class method即可。
#   例:BaseClassName.methodname(self, arguments)
#   要注意,假如是class中的class,就不能這樣做。

# 空的class :  在class定義後加上一行"pass"。 就可以拿來放任何東西,不過這樣做比較難掌握其內容。(是可以靠dir()來列舉...)


class MyClass(int):
    def __init__(self):
        self.lArray = []
       
    def add(self, x):
        self.lArray.append(x)


myClass = MyClass()
print(issubclass(myClass.__class__, int))
print(myClass.__class__ is MyClass)
print(myClass.lArray.__class__.__name__)
print(myClass.add.__self__)
myClass.add.__func__(myClass, "a")
print(myClass.lArray)


print('namespace -------------------------------------')

# Namespace: global --- 此參數定義之後,會影響模組的最上層的區域。
#            nonlocal --- 此參數定義之後,會影響下了該定義的階層的上一層。
# 不建議依賴系統的動態變數解析。(乖乖的傳參數的意思)

def scope_test(): # 第二層
    def do_local(): # 第三層
        spam = "local spam"  # do_local 內部的spam

    def do_nonlocal():
        nonlocal spam # spam 定義在上一層。
        spam = "nonlocal spam" # 此例上一層已定義spam = "test spam", 所以執行此動作的話,第二層的spam會被定義為"nonlocal spam"

    def do_global():
        global spam # spam定義在此模組的第一層
        spam = "global spam" # 這時第一層的spam才被賦值

    spam = "test spam" # 第二層的spam定義。不會影響第一層
    do_local()
    print("After local assignment:", spam)
    do_nonlocal()
    print("After nonlocal assignment:", spam)
    do_global()
    print("After global assignment:", spam)

scope_test()
print("In global scope:", spam)  # 第一層




# iterator
# 只要該物件/class有實作__iter__(self), __next__(self),丟出StopIteration的動作的話,就可以被迴圈用來迭代。
# python原生函式: iterator = iter(obj) --- 獲得該物件的迭代器。
# python原生函式: next(iterator) --- 執行一次迭代。通常會回傳迭代物件
# 下面是一個簡單的可迭代class的實作。
class Reverse:
    """Iterator for looping over a sequence backwards."""
    def __init__(self, data):
        self.data = data
        self.index = len(data)

    def __iter__(self):
        return self

    def __next__(self):
        if self.index == 0:
            raise StopIteration
        self.index = self.index - 1
        return self.data[self.index]
   
print('iterator -------------------------------------')
rev = Reverse('spam')
iter(rev)
for char in rev:
    print(char)

   
   
# generator : 不需定義class就可以產生可迭代物件的方法。
# 只要函式定義裡面使用yield關鍵字(作用是回傳物件),就自動成為可迭代的動作。
def GenReverse(data):
    for index in range(len(data)-1, -1, -1):
        yield data[index]

print('generator -------------------------------------')
genReverse = GenReverse('abc')
print(next(genReverse))
print(next(genReverse))
print(next(genReverse))