python学习日记-2016.7.20

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 包名,即可
如图

python学习日记-2016.7.20_第1张图片
运行截图

而后系统就会自动下载第三方包并保存,你可以在代码中导入调用,如

#-*- 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

你可能感兴趣的:(python学习日记-2016.7.20)