您的位置:首页 > 编程语言 > Python开发

从 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中多重继承

在设计类的继承关系时,通常主线都是单一继承下来的,如果需要混入额外的功能,通常是通过多重继承实现,这种设计称为MixIn

Python中定制类

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 神经网络