รวบรวมเกร็ดการทดลอง นำมาเขียนไว้กันลืม
ที่เหลือ พยายามเขียนลงไว้ใน Python Book แทน เพื่อเอาไว้อ้างอิง
ไพธอนไม่สามารถผ่านค่าไปยังฟังก์ชั่นแบบ "ผ่านค่าโดยการอ้างอิง" ได้
มีวิธีโดยอ้อมคือผ่านค่าโดยใช้ list หรือ dictionary ซึ่งเป็น mutable object
>>> def x(i): ... i[0] += 1 ... >>> i = 5 >>> a = [i] >>> x(a) >>> a [6] >>> i = a[0] >>> i 6
Python มีคำสั่ง exec ที่ใช้ในการแทนค่าตัวแปรแล้วรันคำสั่งแบบพลวัต (dynamicly)
บันทึกพฤติกรรมของ exec ไว้ดังนี้
>>> var='123' >>> a='print %s' % (var) >>> exec a # OR exec(a) 123
>>> var=456 >>> exec a 123 >>> a='print %s' % (var) >>> exec a 456
>>> var='abc' >>> a='print %s' % (var) >>> exec a Traceback (most recent call last): File "<stdin>", line 1, in ? File "<string>", line 1, in ? NameError: name 'abc' is not defined >>> var='"abc"' >>> a='print %s' % (var) >>> exec a abc
ยกเว้นกรณีคำสั่ง import ต้องใช้แบบปกติ
>>> var='re' >>> a='import %s' % (var) >>> exec a >>> dir(re) ['DOTALL', 'I', 'IGNORECASE', 'L', 'LOCALE', 'M', 'MULTILINE', 'S', 'U', 'UNICODE', 'VERBOSE', 'X', '__all__', '__builtins__', '__doc__', '__file__', '__name__', 'compile', 'engine', 'error', 'escape', 'findall', 'finditer', 'match', 'purge', 'search', 'split', 'sub', 'subn', 'template']
อยากได้ดิกฯ แบบที่สามารถเติมข้อมูลได้ ทั้งแนวกว้างและแนวลึก
และให้เป็นข้อมูลแบบ Stack ด้วย
เราใช้วิธีการสร้างลิสต์ในดิกฯ โดย
สร้างคลาสชื่อ ComplexDict
ดังนี้
class ComplexDict: def __init__(self, key): self.__list__ = [] key = str(key) self.__list__.append([key,[]]) def add_key(self, key): if not self.has_key(key): self.__list__.append([key,[]]) def __repr__(self): return repr(dict(self.__list__)) def __getitem__(self, key): _keys = self.keys() if key in _keys: index = _keys.index(key) return self.__list__[index][1] def __delitem__(self, key): if self.has_key(key): self[key] = [] self.__list__.remove([key,[]]) def __setitem__(self, key, val): self.add_key(key) index = self.keys().index(key) self.__list__[index][1] = [val] def keys(self): return [i[0] for i in self.__list__] def values(self): return [i[1] for i in self.__list__] def has_key(self, key): if key in self.keys(): return True else: return False
ทดสอบ
>>> s=ComplexDict('a') >>> s {'a': []} >>> s.add_key('b') >>> s {'a': [], 'b': []} >>> s['a']=1 >>> s['a'].append(2) >>> s {'a': [1, 2], 'b': []} >>> s['a']=2 >>> s {'a': [2], 'b': []} >>> s['b']=ComplexDict('c') >>> s {'a': [2], 'b': [{'c': []}]} >>> s.keys() ['a', 'b'] >>> s.values() [[2], [{'c': []}]] >>> s.__list__ [['a', [2]], ['b', [{'c': []}]]]
ข้อเสียคือยังเข้าถึงข้อมูลยาก
>>> s['b'] [{'c': []}] >>> s['b'][0]['c'] [] >>> s['b'][0]['c'] = 5 >>> s['b'][0]['c'] [5] >>> s {'a': [2], 'b': [{'c': [5]}]} >>>
ข้อมูลชนิดดิกชันนารี่ของไพธอน ดีหลายอย่าง แต่เสียตรงไม่สามารถทำงานแบบ Stack (First in, Last out) ได้
ลองเขียนคลาสง่าย ๆ สร้าง tuple
ใน list
เพื่อเลียนแบบดิกชันนารี่ที่สามารถเก็บข้อมูลเป็น stack ได้
class StackDict: def __init__(self): self.data = [] self._keys = [] self._items = [] def __repr__(self): return repr(dict(self.data)) def __setitem__(self, key, item): if key in self._keys: index = self._keys.index(key) self.data[index] = (key, item) self._items[index] = item else: self.data.append((key, item)) self._keys.append(key) self._items.append(item) def __getitem__(self, key): index = self._keys.index(key) return self._items[index] def __delitem__(self, key): index = self._keys.index(key) self.data.remove((key,self._items[index])) self._keys.remove(key) self._items.remove(self._items[index]) def dict(self): return dict(self.data) def has_key(self, key): if key in self._keys: return True else: return False def keys(self): return self._keys def values(self): return self._items def items(self): return self.data def clear(self): self.__init__() def copy(self): import copy return copy.copy(self) def pop(self, key): item = self[key] del self[key] return item def getkey(self, index): return self._keys[index] def getvalue(self, index): return self._items[index]
เมธอดยังไม่ครบแบบดิกชันนารี่จริง ๆ เราเอาแค่เมธอดที่เรียกใช้บ่อย
ลองทดสอบดู
>>> d = StackDict() >>> d['x']=1 >>> d['a']=2 >>> d['1']=3 >>> d['b']=4 >>> d {'a': 2, 'x': 1, '1': 3, 'b': 4} >>> d.getkey(0) 'x' >>> d.data [('x', 1), ('a', 2), ('1', 3), ('b', 4)] >>> d.getvalue(0) 1 >>> d.getkey(-1) 'b' >>> d.getvalue(-1) 4
default agrument มีประโยชน์ในการกำหนดค่าปริยายให้กับตัวแปรที่ส่งผ่านไปให้ฟังก์ชั่น
ขอยกตัวอย่างเป็นคลาสแทน
>>> class X: ... def __init__(self, a=1, b=2): ... self.a = a ... self.b = b ... print "X.a=%s, X.b=%s" % (self.a, self.b) ... >>> x = X() X.a=1, X.b=2 >>> x = X(11) X.a=11, X.b=2 >>> x = X(a=111) X.a=111, X.b=2 >>> x = X(b=222) X.a=1, X.b=222
ตัวแปร a
และ b
เป็นตัวแปรที่เป็น default argument
การสืบทอดคลาสมักนิยมใช้รูปแบบอาร์กิวเมนต์เป็น *argv **keyw
เพื่อหลีกเลี่ยงการกำหนดอาร์กิวเมนต์ใหม่
โดยไพธอนจะแทนอาร์กิวเมนต์ที่ต้องกำหนดค่าให้ด้วยตัวแปร argv
เป็นข้อมูลชนิด tuple
และแทนอาร์กิวเมนต์ที่มีค่าปริยายกำหนดไว้แล้วด้วยตัวแปร keyw
เป็นข้อมูลชนิด dictionaries
เช่น
class Y(X): def __init__(self, *argv, **keyw): self.__parent = X self.__parent.__init__(self, *argv, **keyw)
ตัวแปรทั้งหมด จะส่งผ่านไปให้คลาส X
เราสามารถดักและเปลี่ยนแปลงค่าในอาร์กิวเมนต์ได้ ดังนี้
>>> class Y(X): ... def __init__(self, *argv, **keyw): ... if keyw.has_key("a") and keyw["a"] > 111: keyw["a"]=111 ... self.__parent = X ... self.__parent.__init__(self, *argv, **keyw) ... >>> y = Y() X.a=1, X.b=2 >>> y = Y(a=999999) X.a=111, X.b=2
ทีแรกจะศึกษาเพื่อเอามาทำ session แต่คิดว่าคงไม่ค่อยเหมาะ เพราะเปลืองหน่วยความจำ
ขอบันทึกไว้หน่อยครับ
สมมุติว่าต้องการทำคำสั่ง print "ABC"
เมื่อเวลาผ่านไป 30 วินาที
แบบแรกใช้โมดูล threading ใช้คำสั่งว่า
>>> def p(): ... print "ABC" ... >>> import threading >>> ss = threading.Timer(30, p) >>> ss.start() (ผ่านไป 30 วินาที) ABC
จับการทำงานของโปรเซสด้วย top ได้ว่า 0.0%CPU 2.7%MEM
มีข้อเสียคือ ออกจากเชลล์ของไพธอนไม่ได้ เพราะต้องรอให้เธรดจบก่อน (ด้วยคำสั่ง ss.cancel()
)
แบบที่สองใช้โมดูล thread เฉย ๆ
ใช้คำสั่งดังนี้
>>> def p(): ... time.sleep(30) ... print "ABC" ... >>> import thread, time >>> ss = thread.start_new_thread(p, ()) (ผ่านไป 30 วินาที) ABC
จับด้วย top ได้ว่า 0.0%CPU 1.1%MEM
ข้อดีคือ ออกจากเชลล์ไพธอนได้เลย (แต่โปรเซสก็ตายด้วย)
ข้อเสียคือ หยุดการทำงานของเธรดไม่ได้
ไพธอนมีคำสั่งในการรันเชลล์ คือ os.system
เช่น สมมุติว่าต้องการรันคำสั่ง ls
>>> import os >>> exitstatus = os.system('ls') FILE1.TXT FILE2.TXT FILE3.TXT >>> exitstatus 0
แต่ถ้าเราต้องการนำเข้าการแสดงผลจากเชลล์ เข้ามาในตัวแปรในไพธอน จะใช้อีกโมดูลนึงคือ commands
เช่น
>>> import commands >>> exitstatus, outtext = commands.getstatusoutput('ls') >>> exitstatus 0 >>> outtext 'FILE1.TXT\nFILE2.TXT\nFILE3.TXT' >>> outtext.split('\n') ['FILE1.TXT', 'FILE2.TXT', 'FILE3.TXT']
การสืบทอดคลาสในไพธอน ถ้าเราสร้างเมธอด __init__
ขึ้นมาใหม่
เขาจะไม่เรียกใช้เมธอด __init__
ของคลาสแม่โดยอัตโนมัติ
เราจึงต้องเขียนสั่งให้เรียกเมธอดของคลาสแม่ด้วยตนเอง
การอ้างถึงคลาสแม่ในไพธอน จะอ้างถึงแบบตรง ๆ เช่น
>>> class X: ... def __init__(self): ... print "print from class X" ... >>> class Y(X): ... def __init__(self): ... X.__init__(self) ... print "print from class Y" ... >>> y = Y() print from class X print from class Y >>>
หรือ
>>> class Y(X): ... def __init__(self): ... self.__parent = X ... self.__parent.__init__(self) ... print "print from class Y" ...
แบบหลังจะมีประโยชน์ในการเรียกใช้คลาสแม่จากเมธอดอื่นในคลาสนั้น ๆ
ถ้าเป็นการสืบทอดคลาสเพียงชั้นเดียว สามารถใช้พรอพเพอร์ตี้ __class__
และ __bases__
คือ
self.__parent = self.__class__.__bases__[0]
แต่พอสืบทอดกันเกินหนึ่งชั้นแล้ว การทำงานจะตีกันมั่ว
มีอีกวิธีนึงในการที่จะให้คลาสลูกเรียกเมธอด __init__
โดยอัตโนมัติ นั่นคือเราจะไม่สร้างเมธอด __init__
ในคลาสลูก แต่สร้างเมธอดใหม่ที่เมธอด __init__
จะมาเรียกใช้อีกทีนึง
ตัวอย่าง
>>> class X: ... def __init__(self,*argv,**keyw): ... if len(self.__class__.__bases__) == 0: ... self._parent = None ... else: ... self._parent = self.__class__.__bases__[0] ... print self._parent ... self.init(argv, keyw) ... def init(self,*argv,**keyw): ... pass ... >>> class Y(X): ... def init(self,*argv,**keyw): ... print 'Print from class Y' ... >>> class Z(Y): ... pass ... >>> y=Y() __main__.X Print from class Y >>> z=Z() __main__.Y Print from class Y