รวบรวมเกร็ดการทดลอง นำมาเขียนไว้กันลืม
ที่เหลือ พยายามเขียนลงไว้ใน 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