python基础3.0

模块:python

引用模块sql

做用域:数据库

在一个模块中,咱们可能会定义不少函数和变量,但有的函数和变量咱们但愿给别人使用,有的函数和变量咱们但愿仅仅在模块内部使用。在Python中,是经过_前缀来实现的。编程

正常的函数和变量名是公开的(public),能够被直接引用,好比:abcx123PI等;app

相似__xxx__这样的变量是特殊变量,能够被直接引用,可是有特殊用途,好比上面的__author____name__就是特殊变量,hello模块定义的文档注释也能够用特殊变量__doc__访问,咱们本身的变量通常不要用这种变量名;函数

相似_xxx__xxx这样的函数或变量就是非公开的(private),不该该被直接引用,好比_abc__abc等;单元测试

之因此咱们说,private函数和变量“不该该”被直接引用,而不是“不能”被直接引用,是由于Python并无一种方法能够彻底限制访问private函数或变量,可是,从编程习惯上不该该引用private函数或变量。测试

private函数或变量不该该被别人引用,那它们有什么用呢?请看例子:ui

def _private_1(name):
    return 'Hello, %s' % name

def _private_2(name):
    return 'Hi, %s' % name

def greeting(name):
    if len(name) > 3:
        return _private_1(name)
    else:
        return _private_2(name)

咱们在模块里公开greeting()函数,而把内部逻辑用private函数隐藏起来了,这样,调用greeting()函数不用关心内部的private函数细节,这也是一种很是有用的代码封装和抽象的方法,即:spa

外部不须要引用的函数所有定义成private,只有外部须要引用的函数才定义为public。

模块搜索路径

当咱们试图加载一个模块时,Python会在指定的路径下搜索对应的.py文件,若是找不到,就会报错:

>>> import mymodule
Traceback (most recent call last): File "<stdin>", line 1, in <module> ImportError: No module named mymodule 

默认状况下,Python解释器会搜索当前目录、全部已安装的内置模块和第三方模块,搜索路径存放在sys模块的path变量中:

>>> import sys >>> sys.path ['', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python36.zip', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6', ..., '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages'] 

若是咱们要添加本身的搜索目录,有两种方法:

一是直接修改sys.path,添加要搜索的目录:

>>> import sys
>>> sys.path.append('/Users/michael/my_py_scripts')

这种方法是在运行时修改,运行结束后失效。

第二种方法是设置环境变量PYTHONPATH,该环境变量的内容会被自动添加到模块搜索路径中。设置方式与设置Path环境变量相似。注意只须要添加你本身的搜索路径,Python本身自己的搜索路径不受影响。

面向对象的OOP

数据封装、继承和多态是面向对象的三大特色。向对象的设计思想是从天然界中来的,由于在天然界中,类(Class)和实例(Instance)的概念是很天然的。Class是一种抽象概念,好比咱们定义的Class——Student,是指学生这个概念,而实例(Instance)则是一个个具体的Student,好比,Bart Simpson和Lisa Simpson是两个具体的Student。因此,面向对象的设计思想是抽象出Class,根据Class建立Instance。面向对象的抽象程度又比函数要高,由于一个Class既包含数据,又包含操做数据的方法。

面向对象最重要的概念就是类(Class)和实例(Instance),必须牢记类是抽象的模板。类是建立实例的模板,而实例则是一个一个具体的对象,各个实例拥有的数据都互相独立,互不影响;方法就是与实例绑定的函数,和普通函数不一样,方法能够直接访问实例的数据;经过在实例上调用方法,咱们就直接操做了对象内部的数据,但无需知道方法内部的实现细节

访问限制:

若是要让内部属性不被外部访问,能够把属性的名称前加上两个下划线__,在Python中,实例的变量名若是以__开头,就变成了一个私有变量(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))
    def get_name(self):
        return self.__name

    def get_score(self):
        return self.__scor

 继承与多态

继承能够把父类的全部功能都直接拿过来,这样就没必要重零作起,子类只须要新增本身特有的方法,也能够把父类不适合的方法覆盖重写。

动态语言的鸭子类型特色决定了继承不像静态语言那样是必须的。

 

获取对象信息:

type判断对象类型

types中定义的常量函数

instance()函数

dir()函数 获取一个对象的全部属性和方法

配合getattr()setattr()以及hasattr(),咱们能够直接操做一个对象的状态

>>> class MyObject(object): ... def __init__(self): ... self.x = 9 ... def power(self): ... return self.x * self.x ... >>> obj = MyObject() 

紧接着,能够测试该对象的属性:

>>> hasattr(obj, 'x') # 有属性'x'吗? True >>> obj.x 9 >>> hasattr(obj, 'y') # 有属性'y'吗? False >>> setattr(obj, 'y', 19) # 设置一个属性'y' >>> hasattr(obj, 'y') # 有属性'y'吗? True >>> getattr(obj, 'y') # 获取属性'y' 19 >>> obj.y # 获取属性'y' 19

实例属性和类属性

实例属性属于各个实例全部,互不干扰;

类属性属于类全部,全部实例共享一个属性;不要对实例属性和类属性使用相同的名字,不然将产生难以发现的错误

python容许动态绑定属性和方法

class Student(object):
    pass

而后,尝试给实例绑定一个属性:

>>> s = Student()
>>> s.name = 'Michael' # 动态给实例绑定一个属性
>>> print(s.name)
Michael

还能够尝试给实例绑定一个方法:

>>> def set_age(self, age): # 定义一个函数做为实例方法
...     self.age = age
...
>>> from types import MethodType
>>> s.set_age = MethodType(set_age, s) # 给实例绑定一个方法
>>> s.set_age(25) # 调用实例方法
>>> s.age # 测试结果
25

使用__slots__

限制实例的属性

好比,只容许对Student实例添加nameage属性。

为了达到限制的目的,Python容许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性:

class Student(object):
    __slots__ = ('name', 'age') # 用tuple定义容许绑定的属性名称

而后,咱们试试:

>>> s = Student() # 建立新的实例
>>> s.name = 'Michael' # 绑定属性'name'
>>> s.age = 25 # 绑定属性'age'
>>> s.score = 99 # 绑定属性'score'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'score'

 

因为'score'没有被放到__slots__中,因此不能绑定score属性,试图绑定score将获得AttributeError的错误。

使用__slots__要注意,__slots__定义的属性仅对当前类实例起做用,对继承的子类是不起做用的:

>>> class GraduateStudent(Student): ... pass ... >>> g = GraduateStudent() >>> g.score = 9999 

除非在子类中也定义__slots__,这样,子类实例容许定义的属性就是自身的__slots__加上父类的__slots__

使用@property

Python内置的@property装饰器就是负责把一个方法变成属性调用的

class Student(object):

    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, value):
        if not isinstance(value, int):
            raise ValueError('score must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!')
        self._score = value

@property的实现比较复杂,咱们先考察如何使用。把一个getter方法变成属性,只须要加上@property就能够了,此时,@property自己又建立了另外一个装饰器@score.setter,负责把一个setter方法变成属性赋值,因而,咱们就拥有一个可控的属性操做

多重继承

 

class Dog(Mammal, RunnableMixIn, CarnivorousMixIn):
    pass

定制类

__str__和__repr__

class Student(object):
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return 'Student object (name=%s)' % self.name
    __repr__ = __str__

 __iter__、__getattr__、__call__

使用枚举类:

from enum import Enum

Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))

 

value属性则是自动赋给成员的int常量,默认从1开始计数。

若是须要更精确地控制枚举类型,能够从Enum派生出自定义类:

from enum import Enum, unique

@unique
class Weekday(Enum):
    Sun = 0 # Sun的value被设定为0
    Mon = 1
    Tue = 2
    Wed = 3
    Thu = 4
    Fri = 5
    Sat = 6

@unique装饰器能够帮助咱们检查保证没有重复值。

访问这些枚举类型能够有若干种方法:

>>> day1 = Weekday.Mon
>>> print(day1)
Weekday.Mon
>>> print(Weekday.Tue)
Weekday.Tue
>>> print(Weekday['Tue'])
Weekday.Tue
>>> print(Weekday.Tue.value)
2
>>> print(day1 == Weekday.Mon)
True
>>> print(day1 == Weekday.Tue)
False
>>> print(Weekday(1))
Weekday.Mon
>>> print(day1 == Weekday(1))
True
>>> Weekday(7)
Traceback (most recent call last):
  ...
ValueError: 7 is not a valid Weekday
>>> for name, member in Weekday.__members__.items():
...     print(name, '=>', member)
...
Sun => Weekday.Sun
Mon => Weekday.Mon
Tue => Weekday.Tue
Wed => Weekday.Wed
Thu => Weekday.Thu
Fri => Weekday.Fri
Sat => Weekday.Sat

 可见,既能够用成员名称引用枚举常量,又能够直接根据value的值得到枚举常量。

 

元类:

type()函数既能够返回一个对象的类型,又能够建立出新的类型,好比,咱们能够经过type()函数建立出Hello类,而无需经过class Hello(object)...的定义。

>>> def fn(self, name='world'): # 先定义函数
...     print('Hello, %s.' % name)
...
>>> Hello = type('Hello', (object,), dict(hello=fn)) # 建立Hello class
>>> h = Hello()
>>> h.hello()
Hello, world.
>>> print(type(Hello))
<class 'type'>
>>> print(type(h))
<class '__main__.Hello'>

要建立一个class对象,type()函数依次传入3个参数:

  1. class的名称;
  2. 继承的父类集合,注意Python支持多重继承,若是只有一个父类,别忘了tuple的单元素写法;
  3. class的方法名称与函数绑定,这里咱们把函数fn绑定到方法名hello上。

metaclass

除了使用type()动态建立类之外,要控制类的建立行为,还可使用metaclass。

metaclass,直译为元类,简单的解释就是:

当咱们定义了类之后,就能够根据这个类建立出实例,因此:先定义类,而后建立实例。

可是若是咱们想建立出类呢?那就必须根据metaclass建立出类,因此:先定义metaclass,而后建立类。

链接起来就是:先定义metaclass,就能够建立类,最后建立实例。

因此,metaclass容许你建立类或者修改类。换句话说,你能够把类当作是metaclass建立出来的“实例”。

 错误、调试和测试

try...except...finally

try:
    print('try...')
    r = 10 / int('2')
    print('result:', r)
except ValueError as e:
    print('ValueError:', e)
except ZeroDivisionError as e:
    print('ZeroDivisionError:', e)
else:
    print('no error!')
finally:
    print('finally...')
print('END')

 出错的时候,必定要分析错误的调用栈信息,才能定位错误的位置。

raise

# err_reraise.py

def foo(s):
    n = int(s)
    if n==0:
        raise ValueError('invalid value: %s' % s)
    return 10 / n

def bar():
    try:
        foo('0')
    except ValueError as e:
        print('ValueError!')
        raise

bar()

Python内置的try...except...finally用来处理错误十分方便。出错时,会分析错误信息并定位错误发生的代码位置才是最关键的。

程序也能够主动抛出错误,让调用者来处理相应的错误。可是,应该在文档中写清楚可能会抛出哪些错误,以及错误产生的缘由。

assert() 断言

def foo(s):
    n = int(s)
    assert n != 0, 'n is zero!'
    return 10 / n

def main():
    foo('0')

 logging

import logging
logging.basicConfig(level=logging.INFO)
s = '0'
n = int(s)
logging.info('n = %d' % n)
print(10 / n)

 断点,调试

注意:断言的开关“-O”是英文大写字母O,不是数字0。

 

单元测试:

class Dict(dict):

    def __init__(self, **kw):
        super().__init__(**kw)

    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(r"'Dict' object has no attribute '%s'" % key)

    def __setattr__(self, key, value):
        self[key] = value

 为了编写单元测试,咱们须要引入Python自带的unittest模块,编写mydict_test.py以下:

import unittest

from mydict import Dict

class TestDict(unittest.TestCase):

    def test_init(self):
        d = Dict(a=1, b='test')
        self.assertEqual(d.a, 1)
        self.assertEqual(d.b, 'test')
        self.assertTrue(isinstance(d, dict))

    def test_key(self):
        d = Dict()
        d['key'] = 'value'
        self.assertEqual(d.key, 'value')

    def test_attr(self):
        d = Dict()
        d.key = 'value'
        self.assertTrue('key' in d)
        self.assertEqual(d['key'], 'value')

    def test_keyerror(self):
        d = Dict()
        with self.assertRaises(KeyError):
            value = d['empty']

    def test_attrerror(self):
        d = Dict()
        with self.assertRaises(AttributeError):
            value = d.empty

 

setUp与tearDown

能够在单元测试中编写两个特殊的setUp()tearDown()方法。这两个方法会分别在每调用一个测试方法的先后分别被执行。

setUp()tearDown()方法有什么用呢?设想你的测试须要启动一个数据库,这时,就能够在setUp()方法中链接数据库,在tearDown()方法中关闭数据库,这样,没必要在每一个测试方法中重复相同的代码:

class TestDict(unittest.TestCase):

    def setUp(self):
        print('setUp...')

    def tearDown(self):
        print('tearDown...')

 文档测试

相关文章
相关标签/搜索