最近毕业设计题目是研究对抗样本,要用tensorflow来搭建神经网络,因此python必不可少,这个不是一个传统的Python学习教程只是把学习Python过程中遇到的问题和经验记录下来(基于Python3.5),如果想要一步一步学习Python建议看下面的网站。
Python学习教程
__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
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
- 千万不要对实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性
- 之前我们可以给实例绑定一个属性,其实还可以绑定一个方法。但是给一个实例绑定的方法,对另一个实例是不起作用的。为了给所有实例都绑定方法,可以给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, 其定义的属性仅仅对当前类实例起作用,对继承的子类不起作用
- 把一个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
- 在设计类的继承关系时,通常主线都是单一继承下来的,如果需要混入额外的功能,通常是通过多重继承实现,这种设计称为MixIn
- 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
"""