1.模块
简而言之,一个.py文件就是一个模块。模块的作用可总结为以下几点:
1.模块可以提高代码的可维护性
2.一次编写就可以被其他地方引用
3.同名函数存在于不同模块不会冲突
4.模块名相同时可以使用不同的包名加以区别
模块的特点:
1.任何模块的第一个字符串都被视为模块的文档注释
2.__author__变量可以写明作者
2.sys模块中的argv
sys.argv[]是一个列表,里面存储的是命令行的参数,它至少拥有一个参数,即argv[0],表示的是py文件的路径,从第二个参数即argv[1]开始存储你在命令行打的其他参数
举例
import sys
def test():
args=sys.argv
if len(args)==1:
print('hello %s'%args[0])
elif len(args)==2:
print('hello,{0}{1}'.format(args[0],args[1]))
elif len(args)==3:
print('hello,{0},{1},{2}'.format(args[0],args[1],args[2]))
if __name__=='__main__':
test()
然后在命令行用下面这个图的方式运行,就会有
由此我们可以看出ou就是argv[1],lalala就是argv[2],也就是命令行的第二和第三个参数
3.函数和变量的作用域
1.正常命名的函数和变量都是公开的(public),如a=1,def b()
2.以双下划线开头和结尾的变量是特殊变量,可以直接被引用,如__author__,__name__
3.以单下划线或双下划线开头的函数和变量是私有的(private),模块外不可见,只有模块内才能引用,如abc,abc,def abc()
4.外部不需要引用的函数全部定义成private,只有外部需要引用的函数才定义为public
5.如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线
,在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))
6.有些时候,你会看到以一个下划线开头的实例变量名,比如_name,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。
双下划线开头的实例变量是不是一定不能从外部访问呢?其实也不是。不能直接访问__name是因为Python解释器对外把__name变量改成了_Student__name,所以,仍然可以通过_Student__name来访问__name变量:
print(bart._Student__name)
#'Bart Simpson'
7.对类的私有变量进行访问或修改使用get和set方法
4.安装第三方模块
python使用包管理工具pip来进行第三方包的下载和管理
pip程序在你的python的安装目录下的Scripts文件夹中,如果你把pip的文件夹路径加入到环境变量Path中,应该就能直接在命令行中输入pip --version可以查看到你的pip的版本信息,如果报错了,那可能有几方面的原因:
1.你安装Python时没有勾选安装Pip,你可以看看你的Scripts文件夹下面有没有pip.exe
2.你没有把pip或python的目录加入换件变量
3.你的电脑中安装了多个pip程序,系统无法知道应用哪一个
我在使用的时候就遇到了这个问题,这时候在命令行中输入
where pip
出现了如上图的情景,我以前安装过的软件中,其中自带了strawberry,里面的perl中也有pip程序,这时候的解决办法是直接cd到你的python的pip.exe的目录下,再输入pip install 包名,即可
如图
而后系统就会自动下载第三方包并保存,你可以在代码中导入调用,如
#-*- coding=utf-8 -*-
'a test module'
from PIL import Image
im=Image.open('F:\a.jpg')
print(im.format,im.size,im.mode)
im.thumbnail((200,100))
im.save('F:\acopy.jpg','JPEG')
上述程序调用了Pillow中的Image为选定图片生成一个缩略图并保存在指定位置
5.类和实例
1.类是抽象的模板,比如Student类,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同。
2.class后面紧接着是类名,即Student,类名通常是大写开头的单词,紧接着是(object),表示该类是从哪个类继承下来的,通常,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。
class Student(object):
pass
3.创建实例后可以自由地给一个实例变量绑定属性,比如,给实例bart绑定一个name属性,和静态语言不同,Python允许对实例变量绑定任何数据,也就是说,对于两个实例变量,虽然它们都是同一个类的不同实例,但拥有的变量名称都可能不同:
>>> bart = Student('Bart Simpson', 59)
>>> lisa = Student('Lisa Simpson', 87)
>>> bart.age = 8
>>> bart.age
8
>>> lisa.ageTraceback (most recent call last): File "", line 1, in
AttributeError: 'Student' object has no attribute 'age'
4.python的__init__方法相当于c++或java的类构造方法,但跟它们又有些许不同,java的构造方法名等于其类名,而python的统一就叫__init__
5.__init__方法的第一个参数永远是self,表示创建的实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。有了__init__方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__方法匹配的参数,但self不需要传,Python解释器自己会把实例变量传进去:
class Student(object):
def __init__(self, name, score):
self.name = name self.score = score
bart = Student('Bart Simpson', 59)
print( bart.name)
#'Bart Simpson'
print(bart.score)
#59
6.多态
1.多态的好处就是,当我们需要传入Dog、Cat、Tortoise……时,我们只需要接收Animal类型就可以了,因为Dog、Cat、Tortoise……都是Animal类型,然后,按照Animal类型进行操作即可。由于Animal类型有run()方法,因此,传入的任意类型,只要是Animal类或者子类,就会自动调用实际类型的run()方法,这就是多态的意思:
2.对于一个变量,我们只需要知道它是Animal类型,无需确切地知道它的子类型,就可以放心地调用run()方法,而具体调用的run()方法是作用在Animal、Dog、Cat还是Tortoise对象上,由运行时该对象的确切类型决定,这就是多态真正的威力:调用方只管调用,不管细节,而当我们新增一种Animal的子类时,只要确保run()方法编写正确,不用管原来的代码是如何调用的。这就是著名的“开闭”原则:
对扩展开放:允许新增Animal子类;
对修改封闭:不需要修改依赖Animal类型的run_twice()等函数。
3.对于静态语言(例如Java)来说,如果需要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,否则,将无法调用run()
方法。对于Python这样的动态语言来说,则不一定需要传入Animal
类型。我们只需要保证传入的对象有一个run()
方法就可以了:
class Timer(object):
def run(self):
print('Start...')
这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。Python的“file-like object“就是一种鸭子类型。对真正的文件对象,它有一个read()方法,返回其内容。但是,许多对象,只要有read()方法,都被视为“file-like object“。许多函数接收的参数就是“file-like object“,你不一定要传入真正的文件对象,完全可以传入任何实现了read()方法的对象。
7.获取对象信息
1.使用type()函数可以得到对象的类型,它可以判断基本类型、函数和类,判断的时候返回的是该类型对应的类的名字,如
>>> type(123)
>>> type('str')
>>> type(None)
>>> type(123)==type(456)
True
>>> type(123)==int
True
>>> type('abc')==type('123')
True
>>> type('abc')==str
True
>>> type('abc')==type(123)
False
2.可以使用isinstance()来判断一个对象是否属于某种类型的,如:
>>> isinstance('a', str)
True
>>> isinstance(123, int)
True
>>> isinstance(b'a', bytes)
True
并且还可以判断一个变量是否是某些类型中的一种,比如下面的代码就可以判断是否是list或者tuple:
>>> isinstance([1, 2, 3], (list, tuple))
True
>>> isinstance((1, 2, 3), (list, tuple))
True
3.如果要获得一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list,比如,获得一个str对象的所有属性和方法:
>>> dir('ABC')
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
4.使用hasattr(),setattr()和getattr()方法可以判断、设置和获取对象的属性
>>> 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
5.type()和isinstance()的区别
type()和isinstance()都能用来判断数据类型,并且type()能做的工作isinstance()都能做,但是type()不能判断类的继承关系,而isinstance()可以,如
class Foo(object):
pass
class Bar(Foo):
pass
print (type(Foo()) == Foo
print (type(Bar()) ==Foo
print(isinstance(Bar(),Foo))
输出
True
False
True
8.MethodType方法详细分析
运行如下的代码
from types import MethodType
def set_age(self,age):
self.age=age
class Stu(object):
pass
Stu.set_age=MethodType(set_age,Stu)
A=Stu()
A.set_age(10)
print(A.age,B.age)#结果都是10
B=Stu()
B.set_age(15)
print(A.age,B.age)#结果都是15
显然之后的
B.set_age(15)
使得A和B的age属性都变成了15,同时没进行运算的B的age属性也变了,这时候我推测是对B的age写入覆盖了A的age属性。(但是这个推测有问题,因为B并未进行运算)
随后运行如下的代码
from types import MethodType
def set_age(self,age):
self.age=age
class Stu(object):
pass
Stu.set_age=MethodType(set_age,Stu)
A=Stu()
A.set_age(10)
B=Stu()
print(A.age,B.age)#结果都是10
这意味的,B在没有进行如下运算的情况下
B.set_age(10)
仍然获得了age属性。
那么我之前的覆盖推测是错的。
接下来运行如下代码。
from types import MethodType
def set_age(self,age):
self.age=age
class Stu(object):
pass
Stu.set_age=MethodType(set_age,Stu)
A=Stu()
B=Stu()
A.set_age(10)
print(id(A.age))#获得A.age的内存地址
B.set_age(15)
print(id(A.age),id(B.age))#获得A.age和B.age的内存地址
结果再运行
A.set_age(10)
之后,显示A.age的地址为XXXXXXXXX。
而在运行
B.set_age(15)
之后,A.age的地址发生了改变,同时B.age的地址与A.age地址一样。
所以,这里A和B的age值其实是同一个值。
参见python帮助文档对MethodType的解释
help(MethodType)
Help on class method in module builtins:
class method(object)
| method(function, instance)
|
| Create a bound instance method object.
关于MethodType的用法是这样说明的:
method(function, instance)
另外有说明"Create a bound instance method object.":创建一个绑定方法的实例。
而在
Stu.set_age=MethodType(set_age,Stu)
填入的实例是"Stu"这个类
这意味着所有的属于Stu这个类的的实例会全部受到Stu.set_age的影响
同时
Stu.set_age=MethodType(set_age,Stu)
中的"Stu.set_age"根本不重要,只是名字罢了,根本起不到对每个实例绑定的方法的作用。
运行如下代码测试
from types import MethodType
def set_age(self,age):
self.age=age
class Stu(object):
pass
TEST=MethodType(set_age,Stu)
A=Stu()
B=Stu()
TEST(10)
print(A.age,B.age)#结果都是10
TEST(15)
print(A.age,B.age)#结果都是15
那么推测是对的,MethodType前的名称对MethodType的生效范围没有任何影响。
而当用
Stu.set_age = set_age
替换
Stu.set_age=MethodType(set_age,Stu)
后
代码变成这样
from types import MethodType
def set_age(self,age):
self.age=age
class Stu(object):
pass
Stu.set_age = set_age
A=Stu()
B=Stu()
A.set_age(10)
B.set_age(15)
print(A.age,B.age)#结果为10 15
显然的,这里"Stu.set_age"的Stu是可以有效的指定到不同的实例的。
结论:MethodType只能用于给实例绑定方法。
当使用MethodType给类绑定方法时,方法是指定到类本身的,换而言之,绑定的方法成为了类本身的一个属性,会影响到整个类,类中的所有元素都会受到影响,获得此属性(包括之后加入此类的)。
9.__slots__
参考:
python__slots__
10.@property
@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
我们在对实例属性操作的时候,就知道该属性很可能不是直接暴露的,而是通过getter和setter方法来实现的。
还可以定义只读属性,只定义getter方法,不定义setter方法就是一个只读属性:
class Student(object):
@property
def birth(self):
return self._birth
@birth.setter
def birth(self, value):
self._birth = value
@property
def age(self):
return 2015 - self._birth