模块:python
引用模块sql
做用域:数据库
在一个模块中,咱们可能会定义不少函数和变量,但有的函数和变量咱们但愿给别人使用,有的函数和变量咱们但愿仅仅在模块内部使用。在Python中,是经过_
前缀来实现的。编程
正常的函数和变量名是公开的(public),能够被直接引用,好比:abc
,x123
,PI
等;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实例添加name
和age
属性。
为了达到限制的目的,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__
。
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个参数:
fn
绑定到方法名hello
上。除了使用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()
方法中关闭数据库,这样,没必要在每一个测试方法中重复相同的代码:
class TestDict(unittest.TestCase): def setUp(self): print('setUp...') def tearDown(self): print('tearDown...')
文档测试