English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Pythonのクラスに動的に属性を追加してオブジェクトを生成する

本文では以下のいくつかの面から一つずつ解決策を提供する

      1プログラムの主要機能

      2実現プロセス

      3クラスの定義

      4生成器generatorを使用して各オブジェクトを動的に更新し、オブジェクトを返す

      5stripを使用して不必要な文字を削除する

      6rematchを使用して文字列をマッチングする

      7timestrptimeを使用して文字列を時間オブジェクトに変換する

      8、完整代码

程序的主要功能

现在有个存储用户信息的像表格一样的文档:第一行是属性,各个属性用逗号(,)分隔,从第二行开始每行是各个属性对应的值,每行代表一个用户。如何实现读入这个文档,每行输出一个用户对象呢?
另外还有4个小要求:

每个文档都很大,如果一次性把所有行生成的那么多对象存成列表返回,内存会崩溃。程序中每次只能存一个行生成的对象。

用逗号隔开的每个字符串,前后可能有双引号(”)或者单引号('),例如”张三“,要把引号去掉;如果是数字,有+000000001.24这样的,要把前面的+和0都去掉,提取出1.24

文档中有时间,形式可能是2013-10-29,也可能是2013/10/29 2:23:56 这样的形式,要把这样的字符串转成时间类型

这样的文档有好多个,每个的属性都不一样,例如这个是用户的信息,那个是通话记录。所以类中的具体属性有哪些要根据文档的第一行动态生成

实现过程

1.类的定义

由于属性是动态添加的,属性-值 对也是动态添加的,类中要含有updateAttributes()和updatePairs()两个成员函数即可,此外用列表attributes存储属性,词典attrilist存储映射。其中init()函数为构造函数。 __attributes前有下划线表示私有变量,不能在外面直接调用。实例化时只需a=UserInfo()即可,无需任何参数。

class UserInfo(object):
 'ユーザー情報を復元するクラス'
 def __init__ (self):
  self.attrilist={}
  self.__attributes=[]
 def updateAttributes(self,attributes):
  self.__attributes=attributes
 def updatePairs(self,values):
  for i in range(len(values)):
   self.attrilist[self.__attributes[i]]=values[i]

2.用生成器(generator)动态更新每个对象并返回对象

生成器相当于一个只需要初始化一次,就可自动运行多次的函数,每次循环返回一个结果。不过函数用return 返回结果,而生成器用yield 返回结果。每次运行都在yield返回,下一次运行从yield之后开始。例如,我们实现斐波那契数列,分别用函数和生成器实现:

def fib(max):
 n, a, b = 0, 0, 1
 while n < max:
  print(b)
  a, b = b, a + b
  n = n + 1
 return 'done'

我们计算数列的前6个数:

>>> fib(6)
1
1
2
3
5
8
'done'

如果用生成器的话,只要把 print 改成 yield 就可以了。如下:

def fib(max):
 n, a, b = 0, 0, 1
 while n < max:
  yield b
  a, b = b, a + b
  n = n + 1

使用方法:

>>> f = fib(6)
>>> f
<generator object fib at 0x104feaaa0>
>>> for i in f:
...  print(i)
... 
1
1
2
3
5
8
>>> 

生成器fib自体がオブジェクトであり、yieldに到達すると中断して結果を返し、次回はyieldの次の行から再開します。生成器はgenerator.next()でも実行できます。

私のプログラムでは、生成器部分のコードは以下の通りです:

def ObjectGenerator(maxlinenum):
 filename='/ホーム/thinkit/ドキュメント/usr_info/USER.csv'
 attributes=[]
 linenum=1
 a=UserInfo()
 file=open(filename)
 while linenum < maxlinenum:
  values=[]
  line=str.decode(file.readline(),'gb2312)#linecache.getline(filename, linenum,'gb2312)
  if line=='':
   print'reading fail! Please check filename!'
   break
  str_list=line.split(',')
  for item in str_list:
   item=item.strip()
   item=item.strip('\"')
   item=item.strip('\'')
   item=item.strip('+0*)
   item=catchTime(item)
   if linenum==1:
    attributes.append(item)
   else:
    values.append(item)
  if linenum==1:
   a.updateAttributes(attributes)
  else:
   a.updatePairs(values)
   yield a.attrilist # 'a' に変更して使用
  linenum = linenum +1

その中で、a=UserInfo()はクラスUserInfoのインスタンスです。ドキュメントはgb2312エンコードされた、上記では対応するデコードメソッドを使用しました。最初の行は属性であり、属性リストをUserInfoに格納する関数があります、つまりupdateAttributes();次の行は属性を格納する必要があります。-値を辞書に読み込んで保存します。p.s. Pythonの辞書はマッピング(map)に相当します。

3.stripを使って不必要な文字を除去する方法

上記のコードから、str.strip(somechar)を使うとstr前後のsomechar文字を除去できます。somecharはシンボルまたは正規表現で、上記のように:

item=item.strip()#文字列前後のすべてのエスケープシーケンスを除去します、例えば\t,\nなど
item=item.strip('\"')#前後の"
item=item.strip('\'')
item=item.strip('+0*')#前後の+00...00,*0の個数は任意で、存在しないこともできます

4.re.matchは文字列をマッチングします

関数文法:

re.match(pattern, string, flags=0)

関数引数説明:

引数           説明

pattern    マッチングする正規表現

string        マッチングする文字列。

flags          マッチング方法を制御するフラグ位です。例えば:大文字小文字を区別するか、複数行マッチングなど。

若しくはマッチ成功した場合、re.matchメソッドはマッチオブジェクトを返します。それともNoneを返します。`

>>> s='2015-09-18'
>>> matchObj=re.match(r'\d{4}-\d{2}-\d{2}', s, flags= 0)
>>> print matchObj
<_sre.SRE_Match オブジェクト at 0x7f3525480f38>
1
2
3
4
5

5.time.strptimeを使って文字列を時間オブジェクトに変換する方法

timeモジュールでは、time.strptime(str, format)を使うとstrをformat形式で時間オブジェクトに変換できます。formatの常用フォーマットには:

     %y 2桁の年表示(00-99)

     %Y 4桁の年表示(000-9999)

     %m 月(01-12)

     %d 月内の日(0-31)

     %H 2424時間制の時間数(0-23)

     %I 1224時間制の時間数(01-12)

     %M 分数(00=59)

     %S 秒(00-59)

さらに、reモジュールを使用して、文字列を正規表現でマッチングし、一般的な時間の形式(YYYY)かどうかを確認する必要があります。/MM/DD H:M:S, YYYY-MM-DD等

上記のコードでは、catchTime関数がitemが時間オブジェクトかどうかを判定し、時間オブジェクトであれば時間オブジェクトに変換します。

以下のコード:

import time
import re
def catchTime(item):
 # check if it's time
 matchObj=re.match(r'\d{4}-\d{2}-\d{2}',item, flags= 0)
 if matchObj!= None :
  item =time.strptime(item,'%Y-%m-%d')
  #print "returned time: %s " %item
  return item
 else:
  matchObj=re.match(r'\d{4}/\d{2}/\d{2}\s\d+:\d+:\d+',item,flags=0 )
  if matchObj!= None :
   item =time.strptime(item,'%Y/%m/%d %H:%M:%S')
   #print "returned time: %s " %item
  return item

完全なコード:

import collections
import time
import re
class UserInfo(object):
 'ユーザー情報を復元するクラス'
 def __init__ (self):
  self.attrilist=collections.OrderedDict()# ordered
  self.__attributes=[]
 def updateAttributes(self,attributes):
  self.__attributes=attributes
 def updatePairs(self,values):
  for i in range(len(values)):
   self.attrilist[self.__attributes[i]]=values[i]
def catchTime(item):
 # check if it's time
 matchObj=re.match(r'\d{4}-\d{2}-\d{2}',item, flags= 0)
 if matchObj!= None :
  item =time.strptime(item,'%Y-%m-%d')
  #print "returned time: %s " %item
  return item
 else:
  matchObj=re.match(r'\d{4}/\d{2}/\d{2}\s\d+:\d+:\d+',item,flags=0 )
  if matchObj!= None :
   item =time.strptime(item,'%Y/%m/%d %H:%M:%S')
   #print "returned time: %s " %item
  return item
def ObjectGenerator(maxlinenum):
 filename='/ホーム/thinkit/ドキュメント/usr_info/USER.csv'
 attributes=[]
 linenum=1
 a=UserInfo()
 file=open(filename)
 while linenum < maxlinenum:
  values=[]
  line=str.decode(file.readline(),'gb2312)#linecache.getline(filename, linenum,'gb2312)
  if line=='':
   print'reading fail! Please check filename!'
   break
  str_list=line.split(',')
  for item in str_list:
   item=item.strip()
   item=item.strip('\"')
   item=item.strip('\'')
   item=item.strip('+0*)
   item=catchTime(item)
   if linenum==1:
    attributes.append(item)
   else:
    values.append(item)
  if linenum==1:
   a.updateAttributes(attributes)
  else:
   a.updatePairs(values)
   yield a.attrilist # 'a' に変更して使用
  linenum = linenum +1
if __name__ == '__main__':
 for n in ObjectGenerator(10):
  print n  # 辞書を出力し、正しいか確認

要約

以上はこの記事の全ての内容であり、皆様の学習や仕事に少しでも役立つことを願っています。何か疑問があれば、コメントを残して交流してください。呐喊教程へのご支援に感謝します。

おすすめ