python中的文献检索之关键词检索

        其实什么文献检索之关键词检索,也就是一道题目,最近在一个论坛上看到的题目,然后就做出来了,但是这道题目对掌握python的基础还是很有帮助的。

        先来说一下这道题目是怎样的,详细如下:

        使用过文献检索系统的同学都知道,在文献检索中我们可以使用关键词进行检索,而且关键词与关键词之间可以用“与”、“或”、“非”的逻辑关系进行组合,并且可以用括号改变“与或非”的优先级顺序。

        通常,我们用“&”表示“与”,其表示的含义为两个关键词同时出现在检索的文本中;用“|”表示“或”,其表示的含义为两个关键词有任一个出现在检索的文本中;用“!”表示“非”,其表示的含义为“!”后紧跟的一个关键词不出现在检索的文本中。它们三者默认的优先级顺序为“!”>“&”> “|”,我们可以用括号改变它们之间的优先级顺序。关键词与“&|!”之间用空格隔开,“!”之后的关键词可以省略空格。

        例如,被检索的文本txt = ‘one apple with two leaves, one is green and the other is yellow.’
我们用s1 = ‘(apple | others) & two’ 进行搜索,返回的结果应该为 True
我们用s4 = ‘!green & (ones | two)’ 进行搜索,返回的结果应该为 False

        现在,请你设计一个函数,给定一个关键词字符串string,在txt中搜索是否能匹配,返回True或者False。

        测试例子为:

#Test sample
txt = 'one apple with two leaves, one is green and the other is yellow.'
s1 = '(apple | others) & two' #True
s2 = 'one & yellow & leaf' #False
s3 = '(!three | one & four) & !five' #True
s4 = '!green & (ones | two)' #False
s5 = '(big | !apple | the) & ((!yellow | !green) | others)' #False

        好了,理解完上面的题目,就开始我们的解题流程了:

        我的思路大概是这样的,用类python缩进写一下伪代码表示为:

find the inner brackets:
while txt match: 
    if txt has !:
        test txt
        replace txt
    if txt has &:
        test txt
        replace txt
    if txt has |:
        test txt   
        replace txt
if txt has !:
     test txt
     replace txt
if txt has &:
    test txt
    replace txt
if txt has |:
    test txt 
    replace txt
return txt

        虽然写得很烂的伪代码,但是总体思想基本表现出来,大概就是:

        先找出最里面的方括号,然后就拿出方括号的内容来进行测试,找出里面的每一个单词,检测字符串中是否包含此单词,如果有!符号,则检测是否不包含此单词,然后将检测结果分别替换为True和False,然后从左向右开始查找是否有&符号,如果有则判断相邻的两个单词(True或False)的结果继续替换True或False,然后继续判断是否有|符号,类似前面那样继续替换为True或False,最后替换到没有括号了,剩下的语句也就像你找到最里面括号的内容那样继续来一遍,最后就可以返回相应答案了。

        说的很长很罗嗦,其实代码可以进一步优化,因为我看到代码重复第二次的时候,我就会认真考虑是否要进行封装了,不过这里就顺着思维直接走一遍,写出了意大利面条式的代码。

        详细的完整写代码思路就不细说了,不过这里主要用到两种方法:

        1.正则搜索:

testInner = re.findall('\([^\(.*^\)]*\)', string)   #搜索最里括号
toList = re.findall('!?\w+', each)   #搜索纯单词
matchAnd = re.findall('\w+\s\&\s\w+',testInner[ind])   #搜索&符号
matchOr = re.findall('\w+\s\|\s\w+',newString)   #搜索|符号

        2.正则匹配替换:

# 定义替换方式
def change(matched):
    toRe = toList[0]   #替换列表的第一个元素
    toList.pop(0)      #去掉列表的第一个元素
    return toRe        #返回数据
testInner[ind] = re.sub('!?\w+', change, each)

        其实正则搜索很容易理解,但是正则匹配这里,我自己用了一个方法,因为我想用自己的一个列表中的每一个元素对应替换正则匹配到的每一个元素,又暂时找不到什么简便的函数,所以就自己想了这个办法,每个传进来的匹配内容,就匹配我的列表的第一个元素,然后我将第一个元素去掉,然后第二个传进来的匹配内容,继续匹配第一个元素,但是实际上是原列表的第二个元素了,所以以此类推,进行了整个列表的替换。

        其实这里面直接替换为True和False其实是一个bug,因为如果测试的语句如果有True或False就会出问题了,可以替换为两个uuid,最后返回才输出布尔值。

        废话少说,直接贴一下最原始最粗糙的整个代码:代码文件

文章在我的github上的地址:点击跳转

原创文章,转载请注明出处!

知识共享许可协议
本文章采用知识共享署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议进行许可。

python中的面向对象编程

        面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)是编程的一种思想,需要用来“类”、“对象”等来实现,其实javascript也可以用面向对象编程,但是它的类是伪类,都是自己封装出来的语法糖,所以这里就说一下python中是如何实现面向对象编程的。

        面向对象有三大特性:封装、继承、多态。

        不想一一举例解释得太详细,因为这些都很基本,简单来说,其实封装就是将某些可能重复使用的代码封装起来,通过不同的场景进行调用;继承就是子类可以继承父类的方法,和儿子继承父亲的特征差不多意思;多态就是无需明确引用的对象是什么,只需要根据不同的对象表现出不同的行为方式,python实现多态和强类型语言有点不同,强类型语言需要指定参数类型,而python不需要,它的对象推断风格为“鸭子类型”,也就是“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子”。

        好了,直接来写一个类,一个子类,和一个实例化的对象:

# 创建一个Person类
class Person(object):
    def __init__(self, name):
        self.name = name

    def who(self):
        print('I am %s' % self.name)

# 创建一个Father类继承Person类
class Father(Person):
    def __init__(self,name,age):
        super(Father,self).__init__(name)
        self.age = age
    
    def who(self):
        print('I am %s, I am %s years old.' % (self.name,self.age))

    def saying(self):
        print('I am Father')

father = Father('Jack',30)
father.who()   #I am Jack, I am 30 years old.
father.saying()   #I am Father

        其实很简单,每个类中都有一个__init__函数用来初始化对象,而所有定义的函数中的self就是用来传入实例化的对象,这个很类似javascript中构造函数的this,其实都大同小异,子类可以继承父类的所有方法,也可以改写父类的方法(who),更可以创建自己的方法(saying)。

        好了,该说说类的成员了,类的成员分为三大类:字段、方法和属性。

        一、字段

        字段包括普通字段和静态字段。举个例子:

# 创建一个Person类
class Person(object):
    action = 'moving'
    def __init__(self,name):
        self.name = name

print(Person.action)   #'moving'
person = Person('Rose')
print(person.name)   #'Rose'

        可以看见,普通字段需要用类来访问,而静态字段要用对象来访问,这也很容易明白,一个函数里面的一个变量,当然通过这个函数来访问,而需要实例化时才传入的变量,当然是通过这个实例化的对象访问。

        二、方法

        方法包括普通方法、静态方法、类方法。举个例子:

# 创建一个Person类
class Person(object):
    action = 'moving'

    def __init__(self,name):
        self.name = name

    def who(self):
        print('I am %s' % self.name)

    @classmethod
    def own(cls):
        print('I am class method and','I am %s' % cls.action)

    @staticmethod
    def static():
        print('I am static method')

person = Person('Kate')
person.who()   #I am Kate
Person.own()   #I am class method and I am moving
Person.static()   #I am static method

        也很容易理解,普通方法就是通过实例化的对象访问的,而类方法和静态方法都是通过类访问,但是类方法要传入自身这个类作为参数。

        三、属性

        属性就只有普通属性一个了,但是却是非常简单的,和普通方法差不多,举个例子:

# 创建一个Person类
class Person(object):
    action = 'moving'

    def __init__(self,name):
        self.name = name

    def who(self):
        print('I am %s' % self.name)

    @property
    def run(self):
        print('I can run')


person = Person('Tom')
person.run   #I can run

        其实和普通方法没太大区别,定义时候加了个@property装饰器,只传入一个self,然后调用时候不用括号。

        不过要说一下属性的调用定义方式。

        类可分为经典类和新式类,而两者的区别就是是否继承自object,例如上面的Person就是新式类,前面的Father就是经典类,经典类定义属性只要前面加@property装饰器一种方法,而新式类,除了这种,还有两种:

# 创建一个Person类
class Person(object):
    action = 'moving'

    def __init__(self, name):
        self.name = name
        self.my_age = 18

    def who(self):
        print('I am %s' % self.name)

    @property
    def age(self):
        print('I am %d years old' % self.my_age)
        return self.my_age

    @age.setter
    def age(self, years):
        self.my_age = years

    @age.deleter
    def age(self):
        del self.my_age
        print('I delete my age now')


person = Person('Jason')
person.age   #I am 18 years old
person.age = 20   #修改年龄为20
person.age   #I am 20 years old
del person.age   #删除了普通字段my_age
person.age   #删除后获取字段值会报错
person.age = 26   #重新设置年龄my_age为26
person.age   #I am 26 years old

        其实很简单,一种访问、一种修改、一种删除。

        其实每一个类成员可分两种形式:公有成员(哪里都能访问)、私有成员(类内部才能访问,外面通过类访问也不行),除了特殊成员(下面讲到)外,在命名时候在前面加两个下划线(例如__what)就变为私有成员了。

        好了,该说一下特殊成员了,也就是特定的一些成员,有:

        __doc__:类的描述信息。

        __module__:当前操作的对象在哪个模块

        __class__:当前操作的对象的类是什么

        __init__:构造方法,通过类创建对象时,自动执行

        __del__:析构方法,对象在内存中被释放时,自动执行

        __call__:实例化后的对象后面加括号,自动执行。

        __dict__:类或对象中的所有成员

        __str__:打印对象输出该方法的返回值

        __getitem__、__setitem__、__delitem__:用于索引操作,分别表示获取、设置、删除数据

        __getslice__、__setslice__、__delslice__:三个分片操作

        __iter__:迭代器

        __new__ 和 __metaclass__:用来表示该类由谁来实例化创建

        关于python的面向对象编程,大概如此。

文章在我的github上的地址:点击跳转

原创文章,转载请注明出处!

知识共享许可协议
本文章采用知识共享署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议进行许可。

python中函数的参数类型

        学习python的过程中,少不了对函数的学习,而python中函数的参数又跟它自身数据类型有很大关系,所以有其自身的独特性,但是毕竟都是编程语言,跟其他语言都有相通的地方,这里就列举一下它的各种参数类型:

        一、位置参数

        就是一个萝卜一个坑,是你自己的就占自己的坑,例如:

def devided(x, y):
    return x / y

print(devided(0, 3))   # 0.0
print(devided(3, 0))   # 报错

        这个位置是x的,你传参数进去,它就对应放在x那里,是y的就会放在y的那里,这就是位置参数。但要注意这个位置参数是必填参数,调用必须传递的。

        二、默认参数

        就是你不传递,它就默认一个参数,这个也很好理解,例如:

def devided(x, y=1):
    return x / y

print(devided(3))   # 3.0

        就是我前面还是按位置参数传递,而到了有默认参数的地方,如果我不传递的话,那它就会按照函数定义的默认值来传值。

        但是这里要注意的是,默认参数必须放在位置参数之后,例如上面函数定义时候不能写devided(x=1, y),就是必传的参数必须放在前面,爱传不传的参数(默认参数)必须放后面,你想一下自己调用函数时候就知道了。

        三、不定长参数

        如果我们传递进去的参数不确定传多少个怎么办,那就需要不定长参数了,例如:

def calc(*numbers):
    sum = 0
    for i in numbers:
        sum += i
    return sum

print(calc(1,3,8))   # 12

        看函数里面的操作,可以知道numbers是一个可迭代对象,实则上就是一个tuple,当然了,你也可以不用到它的所有值,反正就是在不定长参数前面加一个 * 符号就行了。

        这个不定长参数,就和javascript中的函数参数有些区别了,javascript中的函数参数本来就是不定长的,你想传多少都没有问题,因为里面有一个arguments对象保存你传进来的值,所以上面这个例子,用javascript来写可以写成这样:

function numbers() {
    var sum = 0;
    for (i = 0; i < arguments.length; i++) {
        sum += arguments[i];
    }
    return sum;
}

console.log(numbers(1, 3, 8));   // 12

        所以有时候也挺喜欢用javascript这样打补丁的方式添加函数功能,只要判断后面某个参数在不在就行了。

        扯远了,回过头来,我们在传入不定长参数的时候一个一个传挺麻烦的啊,所以也可以在调用的时候传入一个list或者tuple,在前面加个 * 符号就行了,例如:

def calc(*numbers):
    sum = 0
    for i in numbers:
        sum += i
    return sum

inputList = [1,3,8];
print(calc(*inputList))   # 12

        这样多方便,像javascript中的 … 解构符号。

        四、关键字参数

        其实前面写到,js默认就是可变长参数,所以可以轻易扩展函数功能,但是python怎样做的呢,它是通过传入不确定的键值对来达到的,所以这个关键字参数也很好理解,就是key-value这样形式的参数。

        这种key-value在python中就是dict(字典),其实写法类似前面的不定长参数,不过多了一个 * 符号,例如:

def introduction(name, age, **others):
    print('name is:', name)
    print('age is:', age)
    print('others is:', others)
introduction('Jade', 15, sex='female', height='170cm')
# 打印结果为:
# name is: Jade
# age is: 15
# others is: {'sex': 'female', 'height': '170cm'}

        所以也很清楚了,类似上面的不定长参数传参,我们也可以直接传一个dict,像这样:

def introduction(name, age, **others):
    print('name is:', name)
    print('age is:', age)
    print('others is:', others)
details = {'sex':'female', 'height':'170cm'}
introduction('Jade', 15, **details)
# 打印结果为:
# name is: Jade
# age is: 15
# others is: {'sex': 'female', 'height': '170cm'}

        和前面都很类似对吧,但是这里打印的数据中的键值其实没有排序的,所以可能每次打印的顺序都不同,但是键值对还是那样的键值对。

        五、命名关键字参数

        其实我觉得这个名称和作用都很坑爹,因为关键字参数是不限制传入的关键字的,所以如果你要限制怎么办,那就要用命名关键字参数了,它是用一个 * 来分隔的,例如:

def introduction(name, age, *, sex, height):
    print('name is %s, age is %s, sex is %s and heighe is %s' %
          (name, age, sex, height))

details = {'sex': 'female', 'height': '170cm'}
introduction('Jade', 15, **details)
# 打印结果为:
# name is Jade, age is 15, sex is female and heighe is 170cm

# 如果数据多了其他键值对
details = {'sex': 'female', 'height': '170cm', 'weight': '50kg'}
introduction('Jade', 15, **details)   # 报错

        所以可以看到,这个中间的 * 符号就是限制了关键字参数的关键字,所以叫做命名关键字参数(很绕口),那如果中间又有一个可变成参数呢?例如:

def introduction(name, age, *args, sex, height):
    print(name, age, args, sex, height)

        由于可变长参数已经有个 * 符号了,所以后面就无需用 * 符号隔开了。

        好了,这就是python函数中的几种参数类型了,但是要注意的是,参数定义时候要按照这样的顺序:位置参数(必选参数)、默认参数、不定长参数、命名关键字参数和关键字参数。

        由于*args代表不定长参数,而**others代表关键字参数,所以我们可以用通式参数代表所有函数:

        所以可以看到,这个中间的 * 符号就是限制了关键字参数的关键字,所以叫做命名关键字参数(很绕口),那如果中间又有一个可变成参数呢?例如:

def func(*args, **others):
     print(args, others)

文章在我的github上的地址:点击跳转

原创文章,转载请注明出处!

知识共享许可协议
本文章采用知识共享署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议进行许可。

从javascript到python

        python以其简洁优雅闻名,我从学习javascript到学习python的过程中,充分感受到python作者想将一件复杂的事情简单化,或者说,将一段别人长长的语句用一个单词来表达。

        它有很多方便的语法和函数,可以很简便地实现我们需要的做法,然而一些基础的语法,还是和其他语言不同的,所以这里面可能就要硬记一下了。举个例子:

        在javascript中的字符串加数字是这样的,将数字转换为字符串然后直接拼接:

var a = '123'
var b = 321;
console.log(a + b);   //'123321'

        在python中就会报错,说不能隐式地转换整形为字符串:

a = '123'
b = 321;

#TypeError: Can't convert 'int' object to str implicitly
print(a + b);   

        而一些乘法的地方,在javascript中是这样的,先将字符串转换为数字再计算:

var a = '8'
var b = 8;

console.log(a * b);   // 64

        而在python中是这样的,直接将字符串重复8次:

a = '8'
b = 8;

print(a * b);   # '88888888' 

        当然了,这些基本的语法还是和这门语言本身设计的理念有很大关系的,所以这些细小的地方,还是要适应不同语言的理念。

        准备之后一段时间开始写一下关于pyhon的一些文章,当分享也好,笔记也好,都对自己成长有好处的。

        不过写文章之前,还是得好好潜心学习掌握透这门语言,看多点资料和别人的观点,努力弄懂这门语言的真理,加油!

python——有趣的缩进

        最近在学python,它的一大表现特点就是缩进,其实之前用pug(以前的jade)这个模板引擎的时候,已经领教过缩进的招式,缩进其实隐藏着坑的,有很多人都不习惯,很多人都习惯了看花括号,见到缩进感觉很别扭的感觉。

        不过尝试写多两行代码,就会发觉缩进也是挺微妙的,会省很多功夫,而且代码简洁优雅,可能就是python出名的原因之一,很著名的一句话“life is short , you need python”或者“life is short , use python”

        所以又有另外一种问法:你用tab键还是用space键,一个tab顶你4个space喔,当然很多还是推荐用space,不过我觉得就是将tab键设置为4个space键来使用tab键比较好。之前就是有个格式问题报错,大概是:you can use tab or space, but not both,就是说你不要混用,所以如果格式有问题,这也是一个坑。

        通常刚接触的人来说,不习惯的通常在于if语句,或者for循环的地方,跳出了if或者for循环应该写在哪个缩进位置,例如:

def example(x):
    if x < 0:
        return -x
    return x

print(example(-3))  #3

        注意到我写了两个return ,而第二个return是跳出了if语句的,跟它同一缩进位置,证明是平行的关系,当多几个for循环,多几个if和else之后,这种缩进就显得有点微妙了。

        其实缩进减少了很大工作量,而且不要写分号,因为缩进就是要解决掉分号和花括号的,某程度上缩进还是一目了然的,写多两遍,发觉又是另一种景象。