从 Python到Tensorflow 学习之路(三)
2017-12-28 21:39
411 查看
从 Python到Tensorflow 学习之路(四)
最近毕业设计题目是研究对抗样本,要用tensorflow来搭建神经网络,因此python必不可少,这个不是一个传统的Python学习教程只是把学习Python过程中遇到的问题和经验记录下来(基于Python3.5),如果想要一步一步学习Python建议看下面的网站。Python学习教程
模块
在Python中,一个.py文件就称之为一个模块(module),其大大提高了代码的可维护性。使用模块可以避免函数名和变量名冲突,相同名字的函数和变量完全可以分别存在于不同的模块中。
为了避免模块名冲突,Python引入了按照目录来组织模块的方法,称为包(package)。
假设我们有两个模块abc和xyz,与其他模块冲突,我们可以使用包来组织模块,方法是选择一个顶层包名,比如
mycompany按照如下目录存放
那么abc.py模块名就变成了mycompany.abc,类似的xyz.py模块名就变成了mycompany.xyz
注意每一个包目录下都会有一个__init__.py
,这个文件是必须存在的。否则Python就把这个目录当成普通目录,而不是一个包。__init__.py
可以是空文件,也可以有Python代码。因为__init__.py
本身就是一个模块,它的模块名就是mycompany
使用模块"""a test module""" __author__ = 'LWP' import sys def test(): args = sys.argv if len(args)==1: print('%s!' %args[0]) elif len(args)==2: print('Hello, %s!' %args[1]) else: print('Too many arguments!') if __name__=='__main__': test()
使用
sys模块第一步就是导入该模块:
import sys导入后,就有了变量
sys指向该模块,利用
sys这个变量,就可以访问
sys模块的所有功能。
sys模块有一个
argv变量,用list存储了命令行的所有参数。
argv至少有一个参数,因为第一个参数永远是该
.py文件的名称,当我们运行
Python3 blog_test.py获得的
sys.argv就是
[/home/lwp/PycharmProjects/blog_test/blog_test.py!]
注意这个代码:
if __name__ = '__main__': test()
当我们在命令行运行
blog_test模块时,Python解释器会把一个特殊变量
__name__置为
__main__,而如果在其他地方导入该
blog_test模块时,
if判断将失败,因此这种
if测试可以让一个模块通过命令行运行时执行一些额外的代码,最常见的就是运行测试。
Python解释器在读取一个源文件时,会执行从中找到的所有代码。在执行所有代码前,它会定义一些特殊的变量。例如,当Python解释器在运行源文件模块并把它当做主程序,它会将
__name__设置为
'__main__'。如果这个源文件被其他模块导入那么
__name__设置为模块本身的名字。因此你可以将一些不希望被导入的人运行的程序放进检查中,其他人便不会运行
作用域
在一个模块中,我们可能会定义很多的函数和变量,但有的函数和变量我们希望给别人使用,有的函数我们希望仅仅在模块内部使用。在Python中,是通过前缀_实现的
正常的函数和变量名是公开的(public),可以被直接引用,比如
abc, x123
类似与
__xx__这样的前后双下划线变量是特殊变量,可以直接被引用,但是有特殊用途,比如上面的
__author__, __name__
类似于
_xx, __xx这样的前单下划线或单双下划线函数和变量”不应该“被直接引用,而不是”不能“被直接引用,因为Python并没有一种方法可以完全限制访问private函数或者变量。
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)
import blog_test print(blog_test.greeting('world')) # output: Hello, world
面向对象编程
一个简单的处理学生成绩的类
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)) alice = Student('Alice', 99) bob = Student('Bob', 85) alice.print_score() bob.print_score() # output:Alice: 99 # Bob:85
类和实例
class后面紧接着是类名,即Student,类名通常是大写开头的单词,紧接着是(Object),表示该类是从哪个类继承下来的。
由于类可以起到模板的作用,因此可以在创建实例的时候,把一些我们认为必须绑定的属性强制填写进去。通过定义一个特殊的
__init__方法,在创建实例的时候,就把
name, score等属性绑定上去。注意
__init__前后都是双下划线。
__init__方法的第一个参数永远是
self,表示创建的实例本身,因此,在
__init__方法内部,就可以把各种属性绑定到
self,因为
self就指向创建的实例本身。
有了
__init__方法,在创建实例时,不能够传入空的参数,必须传入与
__init__方法相匹配的参数,但是
self不用传,Python解释器自己会把实例变量传进去。
访问限制
从前面Student定义来看,外部的代码仍然可以自由地修改一个实例的name、score属性
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)) alice = Student('Alice', 99) bob = Student('Bob', 85) print(alice.score) # output:99
如果要让内部属性不被外部访问,可以把属性前面的名称加上两个下划线
__,在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)) alice = Student('Alice', 99) bob = Student('Bob', 85) # print(alice.__score) '''Traceback (most recent call last): File "/home/lwp/PycharmProjects/blog_test/blog_test.py", line 17, in <module> print(alice.score) AttributeError: 'Student' object has no attribute '__score''' alice.print_score() # output:Alice: 99
如果外部代码需要获取name和score怎么办,可以增加变量的get方法;如果要允许外部代码修改score怎么办,可以增加变量的set方法。
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 set_name(self, name): self.__name = name def set_score(self, score): if 0 <= score <= 100: self.__score = score else: raise ValueError('Bad Score!') def get_name(self): return self.__name def get_score(self): return self.__score alice = Student('Alice', 99) print(alice.get_name()) # output:Alice print(alice.get_score()) # output:99 alice.set_name('ali') print(alice.get_name()) # output: ali alice.set_score(60) print(alice.get_score()) # output: 60 #alice.set_score(111)
在设置参数方法中我们可以对参数进行类型检查,避免传入无效参数。
在Python中,变量名类似于
__xxx__的,即双下划线开头,双下划线结尾的是特殊变量,特殊变量可以直接访问,不是private变量,所以不能起
__name__, __score__。
以一个下划线开头的变量,比如
_name,这样的实例变量外部是可以访问的,然而其约定“虽然我可以被访问,但是请把我视为私有变量,不要随意访问”
双下划线开头的实例变量也并不是完全不能被访问。不能直接访问
__name是因为Python解释器对外把
__name变量改成了
_Student__name,我们仍然可以通过
_Student__name来访问
__name变量。但是尽量不要这么干!不同版本的Python解释器可能会把
__name改成不同的变量名
... print(alice._Student__name) # output:Alice
注意一种错误写法,这个
__name并不是真的是class内部的
__name,内部的
__name变量已经被Python解释器自动改成了
_Student__name,而外部代码给
alice新增加了一个
__name变量
alice = Student('Alice', 99) print(alice.get_name()) # output:Alice alice.__name = 'Bob' print(alice.__name) # output:Bob print(alice.get_name()) # output:Alice
继承和多态
class Animal(object): def run(self): print('Animal is running......') class Dog(Animal): def run(self): print('Dog is running......') class Cat(Animal): def run(self): print('Cat is running......') a = Animal() a.run() # output:Animal is running..... c = Cat() c.run() # output:Cat is running...... d = Dog() d.run() # output:Dog is running.....
在继承关系中,如果一个实例的数据类型是某个子类,那么它的数据类型也可以被看作是父类。但是,反过来就不行
对于一个变量,我们只需要知道它是
Animal类型,无需确切地知道它的子类型,就可以放心地调用
run()方法,具体调用的
run()方法是作用在哪个对象时,由运行时该对象的确切类型决定,这就是多态。
调用方只管调用,不管细节,当我们新增一种
Animal的子类时,只要确保
run()方法编写正确,不用管原来的代码是如何调用的,这即是著名的开闭原则
对扩展开放:允许新增
Animal子类
对修改封闭:不需要修改依赖
Animal类型的
run_twice()函数
...... def run_twice(animal): animal.run() animal.run() a = Animal() run_twice(a) # output:Animal is running...... # output:Animal is running...... c = Cat() run_twice(c) # output:Cat is running...... # output:Cat is running......
静态语言vs动态语言:
静态语言(比如Java),如果需要传入
Animal类型,则传入的对象必须是
Animal类型或者它的子类,否则将无法调用
run()方法。
对于Python这种动态语言,则不一定需要传入Animal类型。我们只需要保证传入的对象有一个
run()方法即可。这就是动态语言的“鸭子类型。”它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。
...... class Timer(object): def run(self): print('Start.....') ...... run_twice(Timer()) # output:Start..... # output:Start.....
获取对象信息
如何在拿到一个对象的引用时,知道这个对象是什么类型、有哪些方法?可以判断对象的基本类型
如果一个变量指向函数或者类,也可以用type()判断
print(type(123)) # output:<class 'int'> print(type('str')) # output:<class 'str'> print(type(None)) # output:<class 'NoneType'> print(type(abs)) # output:<class 'builtin_function_or_method'>
判断一个对象是否是函数
import types def fn(): pass print(type(fn) == types.BuiltinFunctionType) print(type(abs) == types.BuiltinFunctionType)
isinstance()可以判断一个对象是否是该类型本身,或者位于该类型的父继承链上。
判断一个变量是否是某些类型的一种
print(isinstance([1, 2, 3], (list, tuple))) # output: True print(isinstance({'name': 'Peter', 'age': 20}, (list, tuple))) # output: False
使用
dir()可以获得一个对象的所有属性和方法,它返回一个包含字符串的list
print(dir('ABC')) # output: ['__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']
里面有
__xx__的特殊方法以及
upper()等普通属性或方法
使用
getattr()、setattr()、hasattr()直接操作一个对象状态, 如果试图获取不存在的属性,会抛出AttributeError的错误。可以传入一个default参数,如果属性不存在,就返回默认值
class MyObject(object): def __init__(self): self.x = 9 def power(self): return self.x * self.x obj = MyObject() print(hasattr(obj, 'power')) # output: True print(getattr(obj, 'x')) # output: 9实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性。 print(getattr(obj, 'y', 44)) # output: 44
实例属性和类属性
由于Python是动态语言,根据类创建的实例可以任意绑定属性。给实例绑定属性的方法是通过实例变量,或者通过
self变量
class Student(object): def __init__(self, name): self.name = name s = Student('Bob') s.score = 90
可以直接在class中定义属性,这个属性是类属性,归
Student类所有
class Student(object): name = 'Student' s = Student() print(s.name) # output: Student(因为实例没有name属性,所以会查找class的name属性) print(Student.name) # output: Student(打印类的name属性) s.name = 'Bob' print(s.name) # output: Bob(实例属性的优先级高于类的属性) print(Student.name) # output: Student del s.name print(s.name) # output: Student
千万不要对实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性
使用slots限制定义的属性仅仅对当前类实例起作用
之前我们可以给实例绑定一个属性,其实还可以绑定一个方法。但是给一个实例绑定的方法,对另一个实例是不起作用的。为了给所有实例都绑定方法,可以给class绑定方法class Student(object): pass s = Student() def set_age(self, age): self.age = age from types import MethodType s.set_age = MethodType(set_age, s) s.set_age(25) print(s.age) # output: 25
class Student(object): pass s = Student() def set_age(self, age): self.age = age Student.set_age = set_age s.set_age(11) print(s.age) # output: 11 s2 = Student() s2.set_age(22) print(s2.age) # output: 22
如果我们想限制实例的属性怎么办,我们应该在定义class的时候,定义一个特殊的变量
__slot__变量,来限制该class实例能添加的属性
class Student(object): __slots__ = {'name', 'age'} s = Student() s.age = 55 s.name = 'Bob' print(s.age, s.name) # output: 55 Bob s.score = 100 # AttributeError: 'Student' object has no attribute 'score'
使用score, 其定义的属性仅仅对当前类实例起作用,对继承的子类不起作用
使用@property既可以检查参数又可以用类似于属性这样简单的方式来访问类变量
把一个getter方法变成属性,只需要加上@property即可,此时
@property本身又创建了另外一个装饰器
@score.setter,负责把一个setter方法变成属性赋值
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 s = Student() s.score = 60 print(s.score) # output: 60 s.score = 9999 # ValueError: score must between 0~100!
还可以定义只读属性,只定义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 2017-self._birth s = Student() s.birth = 1996 print(s.age) # output : 21 s.age = 30 # AttributeError: can't set attribute
Python中多重继承
在设计类的继承关系时,通常主线都是单一继承下来的,如果需要混入额外的功能,通常是通过多重继承实现,这种设计称为MixInPython中定制类
Python中打印一个类实例需要自定义__str__()方法, 但是这样仍需要print,直接显示变量调用的不是
__str__()而是
__repr__(),两者的区别在于
__str()__返回的是用户看到的字符串,而
__repr()__返回的是程序开发者看到的字符串。
class Student(object): def __init__(self, name): self.name = name def __str__(self): return 'Student object (name: %s)' % self.name __repr__ = __str__ print(Student('Bob')) # output: Student object (name: Bob) s = Student('Bob')
如果一个类想被用于
for...in..那么必须要实现
__iter__()方法,该方法返回一个迭代对象,Python的for循环会不断调用该迭代对象的
__next__()方法拿到循环的下一个值,知道遇到
StopIteration错误时退出循环。
下面是一个斐波那契数列
class Fib(object): def __init__(self): self.a, self.b = 0, 1 def __iter__(self): return self def __next__(self): self.a, self.b = self.b, self.a+self.b if self.a > 10000: raise StopIteration() return self.a for n in Fib(): print(n)
Fib实例虽然能够作用于for循环,但是不能像list一样支持索引,我们需要实现
__getitem__()方法,但是这个实现没有对slice中的step参数作处理,也没有对负数进行处理
class Fib(object): def __init__(self): self.a, self.b = 0, 1 def __iter__(self): return self def __next__(self): self.a, self.b = self.b, self.a+self.b if self.a > 10000: raise StopIteration() return self.a def __getitem__(self, item): if isinstance(item, int): a, b = 1, 1 for x in range(item): a, b = b, a+b return a if isinstance(item, slice): start = item.start stop = item.stop if start is None: start = 0 a, b = 1, 1 L = [] for x in range(stop): if x >= start: L.append(a) a, b = b, a+b return L f = Fib() print(f[0]) # output: 1 print(f[10]) # output: 89 print(f[1:10]) # output:[1, 2, 3, 5, 8, 13, 21, 34, 55]
正常情况下,我们调用类中方法或者属性时,如果不存在就会报错,但是我们可以通过
__getattr__()方法动态返回一个属性
class Student(object): def __init__(self, name): self.name = name def __getattr__(self, attr): if attr == 'age': return lambda: 25 raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr) s = Student('Bob') print(s.age()) # output: 25 print(s.score()) # AttributeError: 'Student' object has no attribute 'score'
我们可以利用
__call__()方法,直接对实例进行调用, 所以可以把对象看成函数,把函数看成对象。我们可以通过
callable函数来判断一个对象是否能被调用
class Student(object): def __init__(self, name): self.name = name def __call__(self): return ('Student name is %s'% self.name) s = Student('Bob') print(s())
使用枚举类,可以从
Enum派生出自定义类
from enum import Enum, unique @unique class Weekday(Enum): Sun = 0 Mon = 1 Tue = 2 Wed = 3 Thu = 4 Fri = 5 Sat = 6 day1 = Weekday.Mon print(day1) print(Weekday['Tue']) print(Weekday(1)) print(Weekday.Thu.value) for name, member in Weekday.__members__.items(): print(name, ' ==> ', member) """ Weekday.Mon Weekday.Tue Weekday.Mon 4 Sun ==> Weekday.Sun Mon ==> Weekday.Mon Tue ==> Weekday.Tue Wed ==> Weekday.Wed Thu ==> Weekday.Thu Fri ==> Weekday.Fri Sat ==> Weekday.Sat """
我们下期见~
相关文章推荐
- 从 Python到Tensorflow 学习之路(二)
- 从 Python到Tensorflow 学习之路(四)
- [ Tensorflow学习之路 ]——环境配置: tensorflow_CPU + ubuntu16.04(Linux) + Anaconda2-4.2.0 + python2
- 从 Python到Tensorflow 学习之路(一)
- Tensorflow学习笔记---1--Python基本操作
- python学习之路(执行文件调用不同目录下的文件)
- ZhuSuan 是建立在Tensorflow上的贝叶斯深层学习的 python 库
- 我的python学习之路----转换位串到utf-8字符串
- python 爬虫学习之路
- 我的python学习之路-类对象的创建
- 小菜鸟的python学习之路----信息查询系统
- Python 学习之路(一)
- Python学习之路二开发工具eclipse(Eclipse3.X)插件的详细配置
- Python学习之路 (一)开发环境搭建
- Python学习之路——Linux基础之文件系统管理
- python学习之路 四 :文件处理
- 【20170630】TensorFlow与python学习(2)-Mnist入门
- python学习之路-9 socket网络编程
- 王亟亟的Python学习之路(九)-sorted()排序以及简单字符串处理
- 教为学:Python学习之路(二):MySQLdb的几种安装方式,以及用Python测试连接MySql