Python03

模块

Python中,一个.py文件就称之为一个模块(Module)。

为了避免模块名冲突,Python引入了按目录来组织模块的方法,称为包(Package)。

引入了包以后,只要顶层的包名不与别人冲突,那所有模块都不会与别人冲突。如下:abc.py模块的名字就变成了my.abc,类似的,xyz.py的模块名变成了ur.abc。

my
├─ init.py
└─ abc.py
ur
├─ init.py
└─ abc.py

每一个包目录下面必须有一个__init__.py的文件,否则,Python就把这个目录当成普通目录,而不是一个包。
init.py可以是空文件,也可以有Python代码,init.py本身就是一个模块,它的模块名就是my。

可以有多级目录,组成多级层次的包结构。比如如下的目录结构:

my
├─ web
│ ├─ init.py
│ ├─ utils.py
│ └─ www.py
├─ init.py
├─ abc.py
└─ utils.py
文件www.py的模块名为my.web.www,两个utils.py文件的模块名分别为my.utils和my.web.utils。

自己创建模块时要注意命名,不能和Python自带的模块名称冲突。

使用模块

Python模块的标准文件模板:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

' a test module '

__author__ = 'Michael Liao'

import sys

def test():
    args = sys.argv
    if len(args)==1:
        print('Hello, world!')
    elif len(args)==2:
        print('Hello, %s!' % args[1])

if __name__=='__main__':
    test()
  • 第1行和第2行是标准注释
    • 第1行注释可以让这个hello.py文件直接在Unix/Linux/Mac上运行
    • 第2行注释表示.py文件本身使用标准UTF-8编码
  • 第4行是一个字符串,表示模块的文档注释,模块代码的第一个字符串会被视为模块的文档注释
  • 第六行使用__author__变量声明模块的作者

使用模块:

  1. 导入模块:import sys
  2. 通过模块名访问模块的方法和属性:sys.argv

当运行模块文件时,Python解释器把一个特殊变量__name__置为__main__,而如果在其他地方导入该模块时,以下if判断将失败,因此,该if测试可以让一个模块通过命令行运行时执行一些额外的代码,最常见的就是运行测试。

if __name__=='__main__':
    test()

变量命名习惯:

  • 正常变量:公开的,可以被直接引用,如:abc,x123,PI等
  • 特殊变量:可以被直接引用,但是有特殊用途,形如__xxx__,如:__author____name__
  • 私有变量:非公开,不应该被直接引用,形如_xxx__xxx,如_abc__abc等;

安装第三方模块

使用pip安装:pip install numpy
更加好用的方法:使用Anaconda

面向对象编程(OOP)

在Python中,所有数据类型都可以视为对象,每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。

自定义的对象数据类型就是面向对象中的类(Class)的概念,类是对象的模板,定义类:

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 Simpson', 59)
lisa = Student('Lisa Simpson', 87)
bart.print_score()
lisa.print_score()

代码中的bart和lisa称为实例(Instance),实例是类的具体。
对象拥有的函数称为对象的方法(Method)。

所以,面向对象的设计思想是抽象出Class,根据Class创建Instance。

类和实例

OOP中最重要的概念就是类和实例
定义类使用class关键字:class Student(Object): pass

  • Student为类名
  • (Object)表示该Student类继承自Object类

创建实例使用类名+():bart = Student()
Python可以自由的给实例添加属性,如给bart添加name属性:bart.name = 'Bart Simpson'

类作为对象的模板,需要设置一些对象的必备属性,通过类的__init__方法实现:

class Student(Object):
    def __init__(self, name, score):
        self.name = name
        self.score = score

__init__方法的第一个参数永远是self,表示创建的实例本身。
有了__init__方法,在创建对象时必须提供模板设置的属性(除了self,Python解释器会自己传入self):bart = Student('Bart Simpson', 65)

数据封装

在Student类中,每个实例拥有各自的name和score数据。
可以在类内部提供方法来访问这些属性,成为封装:

def print_score(self):
    print('%s: %s' % (self.name, self.score))

bart.print_score()  # Bart Simpson: 65

定义一个方法,除了第一个参数是self外,其他和普通函数一样。

调用一个方法,只需要在实例变量上直接调用,除了self不用传递,其他参数正常传入。

封装还有利于给类添加新的方法。

访问限制

类向外部提供方法访问内部属性,可以隐藏内部的复杂逻辑。
但是上文的Student类的属性仍然可以在外部直接访问或修改。
为了限制内部属性的访问,Python规定类中以__开头的属性名为私有变量,只能在内部被访问。

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))

这样就确保了外部代码不能随意修改对象内部的状态,这样通过访问限制的保护,代码更加健壮。
当需要获取或修改内部的属性时,可以在类中提供对应的方法:

class Student(object):
    ...

    def get_name(self):
        return self.__name

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

继承和多态

继承

在OOP中,当创建新类时,可以选择继承自已有的类,继承可以简化类的实现。新类称为子类,被继承的类为父类。

class Animal(object):
    def run(self):
        print('Animal is running...')

class Dog(Animal):
    pass

上述代码中,定义类Animal类,并提供了run()方法,所以定义Dog类时,只需要继承Animal类,就拥有了Animalrun()方法,无需二次定义。

判断一个变量是否是某个类型可以用isinstance()判断:

isinstance(a, Animal)  # True
isinstance(d, Dog)     # True
isinstance(d, Animal)  # True

因为Dog是从Animal的子类,当我们创建了一个Dog的实例d时,d的数据类型是Dog,但d同时也是Animal。

所以,在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类。

多态

当在子类中重新定义run()方法时,会覆盖父类中的方法,在运行时,会调用子类的run()方法,称为OOP的多态特性。

class Cat(Animal):
    def run(self):
        print('Cat is running...')

可以定义一个函数,函数接收一个对象,执行该对象的run()方法:

def go(animal):
    animal.run()

对于一个变量,我们只需要知道它是Animal类型,无需确切地知道它的子类型,就可以放心地调用run()方法,而具体调用的run()方法是作用Animal或其子类对象上,由运行时该对象的确切类型决定,这就是多态。
并且,新增的任何Animal子类都可以直接传入go()函数,不用对go()方法做任何修改,这就是开闭原则:

  • 对扩展开放:允许新增Animal子类
  • 对修改封闭:不需要修改依赖Animal类型的go()等函数。

静态语言 VS 动态语言

对于静态语言(例如Java)来说,如果需要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,否则,将无法传入go()方法。

对于Python这样的动态语言来说,则不一定需要传入Animal或其子类。只需要保证传入的对象有一个run()方法即可。

这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。

获取对象信息

type()

Python提供了type()函数用于获取对象的类型。
type()函数返回对应的Class类型。
如果在if语句中判断,需要比较两个变量的type类型:type('123') == type(123) # False
判断基本数据类型可以直接写int,str等:type(123) == int # True

如果要判断一个函数对象,可以使用types模块中定义的常量:

import types
def fn():
    pass

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

isinstance()

对于继承关系的class类型来说,可以使用isinstance()函数。
isinstance()判断的是一个对象是否是该类型本身,或者是该类型的子类实例。
比如继承关系:

# Object -> Animal -> Dog
a = Animal
d = Dog()
isinstance(a, Animal)  # True
isinstance(d, Animal)  # True
isinstance(d, Object)  # True
isinstance(a, Dog)     # False

对于基本类型,也可以使用isinstance()判断类型:

isinstance('a', str)     # True
isinstance(123, int)     # True
isinstance(b'a', bytes)  # True

isinstance()还可以判断一个变量是否是某些类型中的一种:

isinstance([1, 2, 3], (list, tuple))  # True
isinstance((1, 2, 3), (list, tuple))  # True

总是优先使用isinstance()判断类型,因为可以同时判断指定类型及其子类

dir()

使用dir()函数会返回一个包含字符串的list,包含对象的所有属性和方法

>>> dir('qwe')
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', ...,
 'upper', 'zfill']

getattr()、setattr()及hasattr()

  • hasattr():用于判断对象是否有指定的方法或属性
  • setattr():用于为对象设置或添加属性
  • getattr():用于获取对象的指定方法或属性,可以传入default参数,若属性不存在,则返回默认值
class MyObject(object):
    def __init__(self):
        self.x = 9
    def power(self):
        return self.x * self.x

obj = MyObject()

hasattr(obj, 'x')       # True
hasattr(obj, 'y')       # False
setattr(obj, 'y', 19)   # 设置一个属性'y'
hasattr(obj, 'y')       # True
getattr(obj, 'y')       # 19
getattr(obj, 'z', 404)  # 404
hasattr(obj, 'power')   # True
getattr(obj, 'power')   # <bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>>

fn = getattr(obj, 'power')
fn()  # 81

实例属性和类属性

Python是动态语言,通过类创建的实例可以动态绑定属性。
给实例添加属性:

  • 通过实例变量:s.name = 'Bert'
  • 在类初始化中通过self变量:def __init__(self, name): self.name = name

给类自身绑定属性:

  • 直接在类中定义属性:class Student(object): name = 'Student'

在类中访问属性:

  • 通过self.name访问实例属性
  • 通过Student.name访问类属性
tag(s): Python 
show comments · back · home