Python基础(2)

目录

  • 函数/function
  • 面向对象编程(类/子类/抽象类)
  • 文件读写
  • 迭代器/迭代对象
  • 生成器/yield

函数/function

函数可以简单理解成 ‘打包’ 好的一段代码。函数的作用主要是为了使代码可以重用。对于一些通用的代码,放到函数里,每次使用直接调就好了。节省时间,提高效率。

Python 里的函数是什么样子的?

先来看一个Python 写的 斐波拉切数列的 函数:

def fib(n):
    a, b = 0, 1
    for i in range(n):
        print(a, end=" ")
        a, b = b, a+b

从例子中可以看到:

  • 函数是以 ‘def ‘ 开头,我们称为 关键字;后面的 ‘fib’ 是函数的名字;紧接着的小括号,里面是函数的参数,可以通过参数从外面传一些值到函数的内部。这里的参数只有一个,我们给函数的参数也起了一个名字’n’ 。
  • 函数的参数可以是一个入上面的例子。也可以一个都没有,只有小括号。也可以是多个,参数之间用逗号隔开。
  • 接下来的  (冒号) 表示 函数的代码写在后面。Python的代码要求以统一的缩进开始。所以上面的函数 换行并缩进了。
  • 上面的函数并没有 返回值 ,Python默认返回了None。 如果要返回确定的值要用return。
  • 调用函数,直接函数名加小括号。有参数写到括号里。如:fib(10)
fib(10)
 #  输出 :  0 1 1 2 3 5 8 13 21 34 

函数的参数可以设置 默认值/缺省参数

def my_sum_2(a, b=5):
    return a+b

my_sum_2(3,10)   #输出 13
my_sum_2(3,) # 第二个参数没有给,所以函数取默认值5。  3+8=8 输出8

Python 函数的参数传递:

  • 不可变类型:类似 c++ 的值传递,如 整数、字符串、元组。如fun(a),传递的只是a的值,没有影响a对象本身。 比如在 fun(a)内部修改 a 的值,只是修改另一个复制的对象,不会影响 a 本身。
  • 可变类型:类似 c++ 的引用传递,如 列表,字典。如 fun(la),则是将 la 真正的传过去,修改后fun外部的la也会受影响

python 中一切都是对象,严格意义我们不能说值传递还是引用传递,我们应该说传不可变对象和传可变对象。

python 传不可变对象实例

def ChangeInt( a ):
    a = 10
    b = 2
ChangeInt(b)
print b # 结果是 2

实例中有 int 对象 2,指向它的变量是 b,在传递给 ChangeInt 函数时,按传值的方式复制了变量 b,a 和 b 都指向了同一个 Int 对象,在 a=10 时,则新生成一个 int 值对象 10,并让 a 指向它。

传可变对象实例

# 可写函数说明
def changeme( mylist ):
   "修改传入的列表"
   mylist.append([1,2,3,4]);
   print("函数内取值: ", mylist)
   return
 
# 调用changeme函数
mylist = [10,20,30];
changeme( mylist );
print("函数外取值: ", mylist)

实例中传入函数的和在末尾添加新内容的对象用的是同一个引用,故输出结果如下:

函数内取值:  [10, 20, 30, [1, 2, 3, 4]]
函数外取值:  [10, 20, 30, [1, 2, 3, 4]]

不定长参数

你可能需要一个函数能处理比当初声明时更多的参数。这些参数叫做不定长参数。

加了星号(*)的变量名会存放所有未命名的变量参数。选择不多传参数也可。如下实例:

# 可写函数说明
def printinfo( arg1, *vartuple ):
   print "输出: "
   print arg1
   for var in vartuple:
      print var
   return;
 
# 调用printinfo 函数
printinfo( 10 );
printinfo( 70, 60, 50 );

以上实例输出结果:

输出:
10
输出:
70
60
50

一个星号和两个星号

#两个 '*' 号
def my_func(x, y=1, *a, **b):
    print(x,y,a,b)   
my_func(1) # 输出 :   1 1 () {}
my_func(1,3) # 输出 : 1 3 () {}
my_func(1,2,3,4,5) #输出 : 1 2 (3, 4, 5) {}
my_func(1,2,3,4,5,tmp=6) #输出 :1 2 (3, 4, 5) {'tmp': 6}

# 可以看出来 一个星号 表示参数是一个tuple,两个星号表示参数为一个字典。
# 可以用type()函数来实验一下 

def my_func(x, y=1, *a, **b):
    print(type(a))
    print(type(b))
    print(x,y,a,b)
my_func(1,2,3,4,5,tmp=6) 
# 输出 :
# <class 'tuple'>
# <class 'dict'>
# 1 2 (3, 4, 5) {'tmp': 6}

匿名函数/lambda

匿名函数,是一个表达式,函数体比def定义的函数简单的多。

语法:

lambda [arg1 [,arg2,.....argn]]:expression

例:

# 匿名函数,以lambda关键字开头,没有了小括号
sum = lambda arg1, arg2: arg1 + arg2;
 
# 调用sum函数
print "相加后的值为 : ", sum( 10, 20 )
print "相加后的值为 : ", sum( 20, 20 )

以上实例输出结果:

相加后的值为 :  30
相加后的值为 :  40

面向对象的编程/class入门

类可以看做是我们自定义的一种数据结构。Python语言自带了一些数据结构(int,float,str,list)。我们可以通过class来定义一下我们自己的类。说起面向对象,首先一定要明白 类和实例的的概念。

类可以看做是 实例 的 ‘图纸'(模板)。

比如:我们盖房子的时候需要’图纸’,盖房子的图纸可以就几张,但是房子却可以盖很多。

也可以看做是写文章时,脑子里想好的大纲。虽然脑子想写什么都想到了。但是终究是还没有’实现’。

这里的 实际存在的房子,动笔写好的稿子,都可以看做是类的’实例’。

等房子盖好了,稿子写好了,我们就可以说我们的 ‘类’ 已经 ‘实现’ 了(创建了’实例’)。

关键字: 类、实例、实现

  • 我们可以把int当做一个类, a = 5,a是就是一个int类型的对象(instance 实例)。int->类,=号->实现,a->一个实例,名字叫做’a’。
  • Python 中的实例被统称为’对象'(object), class就是某一种对象的模板。

创建类

#定义一个类
class Employee(object):
    # python中的__init__表示初始化类。函数中的第一个参数self 是指类的实例自己(即Employee的实例)
    # Python默认会把自己传给初始化的函数的第一个参数,定义初始化函数时一定不要忘了写self参数。
    def __init__(self,name,ID,title,salary,manager=None):
        self.name = name
        self.title = title
        self.salary = salary
        self.manager = manager
    def get_info(self):
        return "Employee name:{},title:{},salary:{},manager:{}"\
    .format(self.name, self.title, self.salary,"" if self.manager==None else self.manager)
    
    def __eq__(self, other):
        return self.ID == other.ID

# 初始化对象/实例
ceo = Employee('user1', 1, 'CEO', 10000000)
user2 = Employee('user2', 10, 'employee', 100, ceo) 

# 通过实例调用类的方法
ceo.get_info()
# 输出: '(Employee name:user1, title:CEO, salary:10000000, manager:)'
user2.get_info()
# 输出: 'Employee name:user2, title:CTO, salary:100, manager:<__main__.Employee object at 0x7fd7affaa940>'

再定义一个类

# 定义一个类
class Company(object):
    def __init__(self, name, employees=[]):
        self.name = name
        self.employees = employees
    
    def hire(self, employee):
        if not employee in self.employees:
            self.employees.append(employee)
    
    def fire(self, employee):
        if employee in self.employees:
            self.employees.remove(employee)
            
    def get_info(self):
        res = "Company:{}, employees:".format(self.name)
        for employee in self.employees:
            res += ", {}".format(employee.get_info())
        return res

# 创建实例
com = Company("com")
com.hire(ceo)
com.hire(user2)
com.get_info()

子类/继承

子类可以继承基类的的field、method,复用代码,也可以再基类的基础上扩展自己特有的field、method。在python中继承中的一些特点:

  • 在继承中基类的构造(__init__()方法)不会被自动调用,它需要在其派生类的构造中亲自专门调用。
  • 在调用基类的方法时,需要加上基类的类名前缀,且需要带上self参数变量。区别于在类中调用普通函数时并不需要带上self参数
  • Python总是首先查找对应类型的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找。(先在本类中查找调用的方法,找不到才去基类中找)。

如果在继承元组中列了一个以上的类,那么它就被称作”多重继承” 。

语法:

派生类的声明,与他们的父类类似,继承的基类列表跟在类名之后,如下所示:

class SubClassName (ParentClass1[, ParentClass2, ...]):
   'Optional class documentation string'
   class_suite

例:

class Parent:        # 定义父类
   parentAttr = 100
   def __init__(self):
      print "调用父类构造函数"
 
   def parentMethod(self):
      print '调用父类方法'
 
   def setAttr(self, attr):
      Parent.parentAttr = attr
 
   def getAttr(self):
      print "父类属性 :", Parent.parentAttr
 
class Child(Parent): # 定义子类
   def __init__(self):
      print "调用子类构造方法"
 
   def childMethod(self):
      print '调用子类方法'
 
c = Child()          # 实例化子类
c.childMethod()      # 调用子类的方法
c.parentMethod()     # 调用父类方法
c.setAttr(200)       # 再次调用父类的方法 - 设置属性值
c.getAttr()          # 再次调用父类的方法 - 获取属性值

以上代码执行结果如下:

调用子类构造方法
调用子类方法
调用父类方法
父类属性 : 200

你可以继承多个类

class A:        # 定义类 A
.....

class B:         # 定义类 B
.....

class C(A, B):   # 继承类 A 和 B
.....

你可以使用issubclass()或者isinstance()方法来检测。

  • issubclass() – 布尔函数判断一个类是另一个类的子类或者子孙类,语法:issubclass(sub,sup)
  • isinstance(obj, Class) 布尔函数如果obj是Class类的实例对象或者是一个Class子类的实例对象则返回true。

方法重写

如果你的父类方法的功能不能满足你的需求,你可以在子类重写你父类的方法

class Parent:        # 定义父类
   def myMethod(self):
      print '调用父类方法'
 
class Child(Parent): # 定义子类
   def myMethod(self):
      print '调用子类方法'
 
c = Child()          # 子类实例
c.myMethod()         # 子类调用重写方法

执行以上代码输出结果如下:

调用子类方法

Abstract Base class(抽象类)

  • 抽象类不能实例化
  • 抽象类中定义的抽象方法,子类必须重写。
from abc import ABC, abstractmethod

class Person(ABC):
    @abstractmethod
    def __init__(self, name, ID):
        self.name  = name
        self.ID = ID
    
    @abstractmethod
    def get_info(self):
        return "name:{}".format(self.name)

上面的Person继承了ABC。定义了两个抽象方法__init__和get_info

class Student(Person):
    def __init__(self, name, ID, level=0):
        super().__init__(name, ID) # 通过super()方法调用基类的方法
        self.level = level
    
    def get_info(self):
        return "{}, level:{}".format(super().get_info(), self.level)# 通过super()方法调用基类的方法
    
    def take_exam(self, grade):
        if grade.upper() in ['A', 'B', 'C']:
            self.level += 1
        return self.level
    
    def graduate(self):
        self.get_info()
        if self.level >= 5:
            print('congratulations, you have graduated from school')
            return True
        else:
            print('Sorry, you need more practice')
            return False

stud_1 = Student('xiaoming', 10, 2)
stud_1.get_info()
# 输出 : 'name:xiaoming, level:2'   

文件读写

Python是用open函数来实现读写文件

#读到一个字符串里
f = open('./ShangHai.txt', 'r', encoding='utf-8') # 使用open读取文件
content = f.read() 
f.close() #关闭文件流

#读取到一个数组里,每一行作为数组的一个元素
f = open('./ShangHai.txt', 'r', encoding='utf-8')
contents = f.readlines() 

# 可以再for中直接循环输出
for line in open('./ShangHai.txt', 'r', encoding='utf-8'):
    print(line.strip())
    print("\n\n")

下面做一个小例子:统计词频

# 定义一个统计词频函数
def my_count(in_file, out_file):
#读取文件并统计词频
    word_count = {}
    for line in open(in_file, encoding='utf-8'):
        words = line.strip().split(" ")
        for word in words:
            if word.lower() in word_count:
                word_count[word.lower()] += 1
            else:
                word_count[word.lower()] = 1
    # 写文件
    out = open(out_file, 'w', encoding='utf-8')
    for word in word_count:
        #print(word, word_count[word])
        out.write(word+" : "+str(word_count[word])+"\n")
    print("count successfully!")
    out.close()

in_file = 'ShangHai.txt'
out_file = 'WordCount.txt'
my_count(in_file, out_file) 

迭代器/可迭代对象

迭代器,简单的可以理解每次只取一个元素,类似 sql中的游标。

迭代是Python最强大的功能之一,是访问集合元素的一种方式。

迭代器是一个可以记住遍历的位置的对象。

迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。

迭代器只能往前不会后退。

迭代器有两个基本的方法:iter() 和 next()

字符串,列表或元组对象都可用于创建迭代器:

list=[1,2,3,4]
it = iter(list)    # 创建迭代器对象
print (next(it))   # 输出迭代器的下一个元素
# 输出 1
print (next(it))
# 输出 2

迭代器对象可以使用常规for语句进行遍历:

ist=[1,2,3,4]
it = iter(list)    # 创建迭代器对象
for x in it:
    print (x, end=" ")
    
# 输出 : 1 2 3 4

实用next()函数,每次只取一个元素:

import sys         # 引入 sys 模块
 
list=[1,2,3,4]
it = iter(list)    # 创建迭代器对象
 
while True:
    try:
        print (next(it))
    except StopIteration:
        sys.exit()

# 输出  
1
2
3
4

生成器yield

  • yield关键字的作用和return相似,都是返回一个值。

不同的是,yield返回值之后,yield后面的代码还会继续执行,return则会直接结束代码。

  • 生成器就是指使用了yield的函数。跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。
  • 在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回yield的值。并在下一次执行 next()方法时从当前位置继续运行。

以下实例使用 yield 实现斐波那契数列:

#!/usr/bin/python3
 
import sys
 
def fibonacci(n): # 生成器函数 - 斐波那契
    a, b, counter = 0, 1, 0
    while True:
        if (counter > n): 
            return
        yield a
        a, b = b, a + b
        counter += 1
f = fibonacci(10) # f 是一个迭代器,由生成器返回生成
 
while True:
    try:
        print (next(f), end=" ")
    except StopIteration:
        sys.exit()

# 输出: 0 1 1 2 3 5 8 13 21 34 55

发表评论

电子邮件地址不会被公开。 必填项已用*标注

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>