从 Python到Tensorflow 学习之路(三)

从 Python到Tensorflow 学习之路(四)


最近毕业设计题目是研究对抗样本,要用tensorflow来搭建神经网络,因此python必不可少,这个不是一个传统的Python学习教程只是把学习Python过程中遇到的问题和经验记录下来(基于Python3.5),如果想要一步一步学习Python建议看下面的网站。
Python学习教程


模块

  • 在Python中,一个.py文件就称之为一个模块(module),其大大提高了代码的可维护性。
  • 使用模块可以避免函数名和变量名冲突,相同名字的函数和变量完全可以分别存在于不同的模块中。
  • 为了避免模块名冲突,Python引入了按照目录来组织模块的方法,称为包(package)。
    假设我们有两个模块abc和xyz,与其他模块冲突,我们可以使用包来组织模块,方法是选择一个顶层包名,比如mycompany按照如下目录存放
    snapshot2.png-2.8kB
    那么abc.py模块名就变成了mycompany.abc,类似的xyz.py模块名就变成了mycompany.xyz

注意每一个包目录下都会有一个__init__.py,这个文件是必须存在的。否则Python就把这个目录当成普通目录,而不是一个包。__init__.py可以是空文件,也可以有Python代码。因为__init__.py本身就是一个模块,它的模块名就是mycompany

使用模块
从 Python到Tensorflow 学习之路(三)_第1张图片

"""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 
    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:

print(type('str'))
# output:

print(type(None))
# output:

print(type(abs))
# output:
  • 判断一个对象是否是函数
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到Tensorflow 学习之路(三))