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

Динамическое добавление свойств и создание объектов в классах Python

Эта статья будет решать проблему по следующим аспектам по одному.

      1. Основная функция программы

      2. Процесс реализации

      3. Определение класса

      4. Использование генератора generator для динамического обновления каждого объекта и возврата объекта

      5. Использование strip для удаления ненужных символов

      6. rematch для соответствия строки

      7. Использование timestrptime для преобразования строки в объект времени

      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='/home/thinkit/Documents/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 #change to ' a ' to use
  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 объект в 0x7f3525480f38>
1
2
3
4
5

5.使用time.strptime提取字符串转换为时间对象

在time模块中,time.strptime(str, format)可以将str按照format格式转换为时间对象,format中的常用格式有:

     %y 两位数的年份表示(00-99)

     %Y 四位数的年份表示(000-9999)

     %m 月份(01-12)

     %d 月中的某一天(0-31)

     %H 24小时制的小时数(0-23)

     %I час по 12-часовому формату (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='/home/thinkit/Documents/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 #change to ' a ' to use
  linenum = linenum +1
if __name__ == '__main__':
 for n in ObjectGenerator(10):
  print n  # Вывод словаря, проверка правильности

Обобщение

Вот и все статьи, надеюсь, они помогут вам в изучении или работе. Если у вас есть вопросы, оставляйте комментарии для обсуждения, спасибо за поддержку呐喊 руководства.

Давай посмотрим, что вам понравится