• Python基础笔记4


    模块

    模块是一组Python代码的集合,一个.py文件就称之为一个模块(Module),按目录来组织模块称为包(Package)。优点:提高了代码的可维护性;避免函数名和变量名冲突。

    mycompany  #包
    ├─ __init__.py  #必需,模块名即为包名
    ├─ abc.py  #abc模块
    └─ xyz.py  #xyz模块
    
    #多级层次的包结构
    mycompany
     ├─ web  #mycompany.web模块
     │  ├─ __init__.py
     │  ├─ utils.py  #mycompany.web.utils
     │  └─ www.py
     ├─ __init__.py
     ├─ abc.py
     └─ utils.py  #mycompany.utils
    

    创建模块时不能和Python自带的模块名称冲突(检查模块是否存在用import abc),否则将无法导入系统自带的模块。

    1.使用模块

    Python模块的标准文件模板,编写一个hello.py模块:

    #! /usr/bin/env python3   #让文件直接在Unix上运行
    # _*_ coding: utf-8 _*_  #使用utf-8编码
    
    'a test module'  #模块的文档注释
    
    __author__='Jesse Peng'  #作者变量
    
    import sys  #导入sys模块后,变量sys可以访问该模块所有功能
    
    def test():
        args = sys.argv #argv变量用list存储了命令行的所有参数
        if len(args)==1: #第一个参数是.py文件的名称
            print('hello, world!')
        elif len(args)==2:
            print('hello, %s!' % args[1])
        else:
            print('too many arguments!')
            
    if __name__=='__main__':  #这种if测试可以让一个模块通过命令行运行时执行一些额外的代码,最常见的就是运行测试。
        test()
    

    命令行运行:

    python3 hello.py
    python3 hello.py Jesse
    

    交互环境运行:

    import hello
    hello.test() #需要调用test函数
    

    作用域
    正常的函数和变量名是公开的(public),可被直接引用,如abc,x123,PI。
    特殊变量__xxx__,如__author__,name
    非公开变量或函数(private),不应该被直接引用,如_abc,__abc。

    def _private1(name): #私有函数
        return 'hello, %s' % name
    
    def _private2(name):  #私有函数
        return 'Hi, %s' % name
    
    def greeting(name):  #公有函数
        if len(name)>3:
            return _private1(name)
        else:
            return _private2(name)
    

    上例中调用公有函数greeting()不用关心内部的私有函数细节,这是一种非常有用的代码封装和抽象的方法。
    外部不需要引用的函数全部定义成private,只有外部需要引用的函数才定义为public。

    2.安装第三方模块

    包管理工具pip。Linux上若并存Python3和Python 2,Python3用pip3。

    pip install Pillow
    

    安装常用模块。
    使用anaconda,已经内置了许多第三方库。
    默认情况下,Python解释器会搜索当前目录、所有已安装的内置模块和第三方模块,搜索路径存放在sys.path变量中。

    若要添加自己的搜索目录,一是通过sys.path添加:

    import sys
    sys.path.append('/your/pypath')
    

    二是设置环境变量PYTHONPATH.

    面向对象编程OOP

    面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行。
    面向对象的程序设计把计算机程序视为一组对象的集合,对象直接接收并处理消息,计算机执行对象间传递的消息。
    所有数据类型都可为对象,也可自定义对象数据类型,也就是类(Class)的概念。
    举例比较两者
    面向过程:

    std={'name':'Jesse','score':90}
    def print_score(std):
        print('%s: %s' % (std['name'],std['score']))
    

    面向对象:

    class Student(object):
        def __init__(self, name, score):
            self.name = name #对象Student的name属性
            self.score = score #对象Student的score属性
            
        def print_score(self):  #让对象自己把自己的数据打印出来。
            print('%s: %s' % (self.name, self.score))
    

    对象的方法(Method):调用对象对应的关联函数。

    bart = Student('bart',59)
    lisa = Student('lisa',87)
    bart.print_score()
    lisa.print_score()
    

    面向对象的设计思想是抽象出Class,根据Class创建Instance。
    类(Class)是一种抽象概念,如我们定义的Class——Student,指学生这个概念。一个Class既包含数据,又包含操作数据的方法。
    实例(Instance)则是一个个具体的Student,如bart和lisa,各个实例拥有的数据都互相独立,互不影响。
    数据封装、继承和多态是面向对象的三大特点。

    1.类和实例

    定义类:
    类名通常首字母大写的单词,(object)表该类从哪个类继承下来的。如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。

    class Student(object):
        pass
    

    创建实例:
    通过类名()实现

    bart = Student()
    bart  #instance
    Student  #class
    
    #自由给实例变量绑定属性
    bart.name = 'bart'
    bart.name
    

    类可以起到模板的作用,创建实例时可通过定义一个特殊的__init__把必须绑定的属性强制加进去。

    class Student(object):
        def __init__(self, name, score):
            self.name = name
            self.score = score
            
    #调用
    bart = Student('bart',59) #不用再传入self,不能为空
    bart.name
    bart.score
    

    __init__方法的第一个参数永远是self,表示创建的实例本身。仍然可以在类的方法中用默认参数、可变参数、关键字参数和命名关键字参数。

    方法就是与实例绑定的函数,和普通函数不同,方法可以直接访问实例的数据。

    数据封装
    在上面的Student类中,每个实例就拥有各自的name和score数据,没有必要从外面的函数去访问,可以直接在Student类的内部定义访问数据的函数,这样就把“数据”给封装起来了。这些封装数据的函数是和Student类本身是关联起来的,我们称之为类的方法。

    class Student(object):
    
        def __init__(self, name, score):
            self.name = name
            self.score = score
    
        def print_score(self):
            print('%s: %s' % (self.name, self.score))
            
    #调用
    bart.print_score()
    

    封装使得调用很容易,且不用知道内部实现的细节。另一个好处是可以给类增加新的方法。

    class Student(object):
        ...
    
        def get_grade(self):  #新增
            if self.score >= 90:
                return 'A'
            elif self.score >= 60:
                return 'B'
            else:
                return 'C'
    
    #调用
    lisa = Student('Lisa', 99)
    print(lisa.name, lisa.get_grade())
    

    2.访问限制

    实例的变量名以__开头就是私有变量(private),只有内部可以访问,外部不能访问。确保了外部代码不能随意修改对象内部的状态,这样通过访问限制的保护,代码更加健壮。

    class Student(object):
        def __init__(self, name, score):
            self.__name=name
            self.__score=score
        def print_score(self):
            print('%s: %s' % (self.__name, self.__score))
    #调用
    bart=Student('bart', 59)
    bart.__name  #error
    

    要想外部再获取name和score,可给类增加新的获取方法:

    class Student(object):
        .......
        def get_name(self):
            return self.__name
        def get_score(self):
            return self.__score
    

    给类再增加允许外部修改score的方法:

        def set_score(self, score):
            self.__score = score
    

    为什么不直接进行bart.score=99修改,非要定义一个私有变量的方法?因为这样可以对参数做检查,避免无效的参数:

    class Student(object):
        ......
        def set_score(self, score):
            if 0 <= score <= 100:
                self.__score = score
            else:
                raise ValueError('bad score')
    

    __xxx__在Python中是特殊变量,不同于私有变量,可直接访问。而_name(一个下划线)变量外部也可访问,但尽量将它视为私有变量,不要随意访问。
    实例中私有变量外部也是可以强制使用的,但不要这么做:

    bart._Student__name  #bart
    

    比较一下:

    bart.__name = 'new name'
    bart.__name  #new name
    #外部新设置了一个变量__name,而内部的__name并未改变
    
    bart.get_name()  #bart
    

    3.继承和多肽

    父类、子类

    #父类
    class Animal(object):
        def run(self):
            print('animal is running')
    
    #子类
    class Dog(Animal):
        pass
    class Cat(Animal):
        pass
    

    子类继承了父类的全部功能。

    dog=Dog()
    dog.run()  #来自父类的run方法:animal is running
    
    cat=Cat()
    cat.run()
    

    也可对子类增加新的方法:

    class Dog(Animal):
        def run(self):  #新增方法1
            print('dog is running')
        def eat(self):  #新增方法2
            print('eating meat')
    

    当子类和父类存在相同名字的方法时(如上例run方法),子类会覆盖父类,不同子类拥有不同方法,这就是多态

    类其实也是一种数据类型:

    a = list() #a是list类型
    b = Animal() #b是Animal类型
    c = Dog() #c是Dog类型
    
    #判断变量是否为某种类型:
    isinstance(a,list)
    isinstance(b,Animal)
    isinstance(c,Dog)
    
    isinstance(c,Animal) #True
    isinstance(b,Dog) #False
    

    编写一个接受Animal类型变量的函数来理解多态:

    def run_twice(animal):
        animal.run()
        animal.run()
    
    #以下都是Animal类型    
    run_twice(Animal())  #Animal is running/Animal is running
    run_twice(Cat())  #Cat is running/Cat is running
    run_twice(Dog())  #Dog is running/Dog is running
    

    传入的类型只要是Animal类或子类,就会自动调用实际类型的run()方法,这就是多态的意思。

    多态真正的威力:调用方只管调用,不管细节,而当我们新增一种Animal的子类时,只要确保run()方法编写正确,不用管原来的代码是如何调用的。这就是著名的“开闭”原则:对扩展开放:允许新增Animal子类;对修改封闭:不需要修改依赖Animal类型的run_twice()等函数。

    继承可以一级一级地继承下来,而任何类最终都可以追溯到根类object。

    Python作为动态语言,它的“file-like object“是一种“鸭子类型”,并不要求严格的继承体系。如上不一定需要传入Animal类型,只需要保证传入的对象有一个run()方法就可以了。

    class Timer(object):
       def run(self):
           print('start...')
    

    4.获取对象信息

    type()函数返回对应的Class类型

    #基本类型
    type(123)
    type('str')
    type(None)
    
    #指向函数或类的变量
    type(abs)
    type(a)
    
    type(123)=int #True
    type('abc')=type(123) #False
    

    判断一个对象是否为函数:

    import types
    def fn():
        pass
    
    type(fn)==types.FunctionType  #True
    type(abs)==types.BuiltinFunctionType
    type(lambda x: x)==types.LambdaType
    type((x for x in range(10)))==types.GeneratorType
    

    isinstance函数

    #创建3种类型对象:
    a=Animal()
    d=Dog()
    h=Husky()
    
    #判断:
    isinstance(h, Dog)
    isinstance(h, Animal)
    
    #type判断的类型也可用isinstance:
    isinstance('abc',str)
    #判断一个变量是否是某些类型中的一种
    isinstance([1, 2, 3], (list, tuple))
    isinstance((1, 2, 3), (list, tuple))
    

    dir()函数
    获得一个对象的所有属性和方法,返回list。

    dir("abc")
    

    len()函数内部自动调用该对象的__len__()方法:

    len('abc')
    'abc'.__len__() #效果同上
    

    其他都是普通属性或方法,如lower:

    'ABC'.lower() #abc
    

    直接操作一个对象的状态:

    #定义一个对象
    class MyObject(object):
        def __init__(self):
            self.x=9
        def power(self):
            return self.x * self.x
    
    obj = MyObject()
    
    #测试对象的属性:
    hasattr(obj, 'x') #True 是否有x属性
    obj.x  #9
    hasattr(obj, 'y') #False
    setattr(obj, 'y', 19) #设置一个y属性
    hasattr(obj, 'y') #True
    getattr(obj, 'y') #19 获取y属性
    obj.y #19
    
    getattr(obj, 'z', 404) # 获取属性'z',如果不存在,返回默认值404
    
    #获取对象的方法:
    hasattr(obj, 'power')
    getattr(obj, 'power')
    fn = getattr(obj, 'power')
    fn # fn指向obj.power
    fn() # 同obj.power()
    

    一个例子:

    def readImage(fp):
        if hasattr(fp, 'read'):
            return readData(fp)
        return None
    

    5.实例属性和类属性

    根据类创建的实例可以任意绑定属性,方法是通过实例变量或self变量。

    类属性直接在class中定义,这个属性虽然归类所有,但类的所有实例都可以访问到。实例属性属于各个实例所有,互不干扰。

    class Student(object):
        name = 'Student'
    
    s=Student() #创建实例s
    print(s.name)  #Student
    print(Student.name) #Student
    
    s.name='Jesse' # 给实例绑定name属性
    print(s.name) #Jesse 实例属性优先级比类属性高
    print(Student.name) #Student
    
    del s.name
    print(s.name) #Student
    

    从上看出,最好不要对实例属性和类属性使用相同的名字。

  • 相关阅读:
    shell 知识点
    辅助字符串处理类:org.apache.commons.lang3.StringUtils
    post请求(headers里有属性)报错:Request header field xxx is not allowed by Access-Control-Allow-Headers in preflight response
    vue-cli 打包报错:Unexpected token: punc (()
    遍历对象,并对其中第一个(随机)进行处理
    JavaScript中类似PHP的uniqid()方法
    使用crypto-js的md5加密
    Yarn、MapReduce、spark、storm的关系
    hadoop 知识点
    spring cloud 知识点
  • 原文地址:https://www.cnblogs.com/jessepeng/p/11235060.html
Copyright © 2020-2023  润新知