python: เกร็ดความรู้ของภาษา

รวบรวมเกร็ดการทดลอง นำมาเขียนไว้กันลืม

ที่เหลือ พยายามเขียนลงไว้ใน Python Book แทน เพื่อเอาไว้อ้างอิง

Topic: 

python: pass by reference

ไพธอนไม่สามารถผ่านค่าไปยังฟังก์ชั่นแบบ "ผ่านค่าโดยการอ้างอิง" ได้
มีวิธีโดยอ้อมคือผ่านค่าโดยใช้ 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

Python มีคำสั่ง exec ที่ใช้ในการแทนค่าตัวแปรแล้วรันคำสั่งแบบพลวัต (dynamicly)
บันทึกพฤติกรรมของ exec ไว้ดังนี้

การใช้งาน
>>> var='123'
>>> a='print %s' % (var)
>>> exec a    # OR exec(a)
123
การกำหนดตัวแปร var ใหม่ ไม่สามารถเปลี่ยนแปลงการแทนค่าจากครั้งก่อน ต้องกำหนดใหม่ทั้งสองครั้ง
>>> var=456
>>> exec a
123
>>> a='print %s' % (var)
>>> exec a
456
การแทนค่า ไพธอนจะแทนค่าทุกสิ่งทุกอย่างของตัวแปร ลงไปในคำสั่ง
เวลาใช้กับตัวแปร string ต้องใส่เครื่องหมาย quot ซ้อนด้วย
>>> 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']
Topic: 

python: ทำ ComplexDict แบบนึง

อยากได้ดิกฯ แบบที่สามารถเติมข้อมูลได้ ทั้งแนวกว้างและแนวลึก
และให้เป็นข้อมูลแบบ 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]}]}
>>> 

python: ทำ StackDict

ข้อมูลชนิดดิกชันนารี่ของไพธอน ดีหลายอย่าง แต่เสียตรงไม่สามารถทำงานแบบ 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

python: บันทึกเรื่อง default argument

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

python: บันทึกเรื่อง thread

ทีแรกจะศึกษาเพื่อเอามาทำ 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
ข้อดีคือ ออกจากเชลล์ไพธอนได้เลย (แต่โปรเซสก็ตายด้วย)
ข้อเสียคือ หยุดการทำงานของเธรดไม่ได้

Topic: 

python: บันทึกเรื่องการรันเชลล์

ไพธอนมีคำสั่งในการรันเชลล์ คือ 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']

อ้างอิง
Google Groups: Command shell access within Python

Topic: 

python: บันทึกเรื่องคลาส

การสืบทอดคลาสในไพธอน ถ้าเราสร้างเมธอด __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]

แต่พอสืบทอดกันเกินหนึ่งชั้นแล้ว การทำงานจะตีกันมั่ว

python: บันทึกเรื่องคลาสอีกครั้ง

มีอีกวิธีนึงในการที่จะให้คลาสลูกเรียกเมธอด __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