Day07的课程要点记录
详细教程地址:Day7 - 面向对象编程进阶
一、静态方法、类方法、属性方法
1.1 静态方法 @staticmethod
通过@staticmethod
装饰器,可把其装饰的方法变为一个静态方法。
静态方法是不可以访问实例变量或类变量的。
一个不能访问实例变量和类变量的方法,跟类本身已经没什么关系了,它与类唯一的关联就是需要通过类名来调用这个方法。
只是名义上归类管理,但上在静态方法里访问不了类或实例中的任何属性。
class Dog(object):
def __init__(self, name):
self.name = name
@staticmethod
def eat(self):
print("%s is eating" % (self.name))
d = Dog("Wangcai")
d.eat()
以上调用会出错,提示eat
需要一个self
参数,但调用时却没有传递。
没错,当eat变成静态方法后,实例调用时就不再把实例本身当作一个参数传给self了。
TypeError: eat() missing 1 required positional argument: 'self'
想让上面的代码可以正常工作有两种办法
- 调用时主动传递实例本身给eat方法,即d.eat(d)
- 在eat方法中去掉self参数,但这也意味着,在eat中不能通过self.调用实例中的其它变量了
class Cat(object):
def __init__(self, name):
self.name = name
@staticmethod
def eat():
print("%s is eating %s" % ("Kitty", "fish"))
@staticmethod
def talk(self):
print("%s is talking" % self.name)
c = Cat("Meo")
c.eat()
c.talk(c)
1.2 类方法 @classmethod
类方法通过@classmethod
装饰器实现,类方法和普通方法的区别是:
类方法只能访问类变量,不能访问实例变量
class Dog(object):
def __init__(self, name):
self.name = name
@classmethod
def eat(self):
print("%s is eating %s" % (self.name, "food"))
d = Dog("Doggy")
d.eat()
执行后报错,说Dog没有name属性,因为name是个实例变量,类方法是不能访问实例变量的
AttributeError: type object 'Dog' has no attribute 'name'
此时可以定义一个类变量,也叫name。
class Dog(object):
name = 'Wangwang'
def __init__(self, name):
self.name = name
@classmethod
def eat(self):
print("%s is eating %s" % (self.name, "food"))
d = Dog("Doggy")
d.eat()
# 执行效果
Wangwang is eating food
1.3 属性方法 @property
属性方法:通过@property把一个方法变成一个静态属性
class Dog(object):
def __init__(self, name):
self.name = name
@property
def eat(self):
print("%s is eating %s" % (self.name, "food"))
d = Dog("Doggy")
d.eat()
调用会出以下错误, 说NoneType is not callable。
TypeError: 'NoneType' object is not callable
因为eat此时已经变成一个静态属性了,不是方法了,想调用已经不需要加()号了,直接d.eat就可以了
class Dog(object):
def __init__(self, name):
self.name = name
@property
def eat(self):
print("%s is eating %s" % (self.name, "food"))
d = Dog("Doggy")
d.eat
# 输出
Doggy is eating food
class Dog(object):
def __init__(self, name):
self.name = name
self.__food = None # 设置私有属性 food 为 None
@property # 设置属性方法,将方法变为静态属性
def eat(self):
print("%s is eating %s" % (self.name, self.__food))
@eat.setter # 为静态属性传值
def eat(self, food):
print("set %s to food %s" % (self.name, food))
self.__food = food # 将值赋给私有属性 food
@eat.deleter # 删除属性方法,普通方式无法删除。
def eat(self):
del self.__food
print("It is deleted.")
d = Dog("Doggy")
d.eat
d.eat = 'pork'
d.eat
del d.eat # 删除属性方法
二、类的特殊成员方法
2.1 __doc__
表示类的描述信息
class Dog(object):
"""This is a class for dog."""
def __init__(self, name):
self.name = name
print(Dog.__doc__)
#输出:类的描述信息
2.2 __module__
和 __class__
__module__
表示当前操作的对象在哪个模块
__class__
表示当前操作的对象的类是什么
class C:
def __init__(self):
self.name = 'Will'
from lib.aa import C
obj = C()
print(obj.__module__) # 输出 lib.aa,即:输出模块
print(obj.__class__) # 输出 lib.aa.C,即:输出类
2.3 __init__
构造方法,通过类创建对象时,自动触发执行。
2.4 __del__
析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的
2.5 __call__
对象后面加括号,触发执行。
构造方法的执行是由创建对象触发的,即:对象 = 类名() ;
而对于 call 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo:
def __init__(self):
pass
def __call__(self, *args, **kwargs):
print("call", args, kwargs)
obj = Foo() # 执行 __init__
obj(1234, name='abc') # 执行 __call__
2.6 __dict__
查看类或对象中的所有成员
class Dog(object):
"""This is a class for dog."""
def __init__(self, name):
self.name = name
d = Dog("Wangwang")
print(Dog.__dict__)
# 输出:{'__module__': '__main__', '__doc__': 'This is a class for dog.', '__init__': , '__dict__': , '__weakref__': }
print(d.__dict__)
# 输出:{'name': 'Wangwang'}
2.7 __str__
如果一个类中定义了str方法,那么在打印对象时,默认输出该方法的返回值。
class Foo:
def __str__(self):
return 'alex li'
obj = Foo()
print obj
# 输出:alex li
2.8 __getitem__
, __setitem__
, __delitem__
用于索引操作,如字典。以上分别表示获取、设置、删除数据
class Foo(object):
def __getitem__(self, key):
print('__getitem__',key)
def __setitem__(self, key, value):
print('__setitem__',key,value)
def __delitem__(self, key):
print('__delitem__',key)
obj = Foo()
result = obj['k1'] # 自动触发执行 __getitem__
obj['k2'] = 'alex' # 自动触发执行 __setitem__
del obj['k1']
2.9 __new__
, __metaclass__
class Foo(object):
def __init__(self,name):
self.name = name
obj = Foo("alex")
上述代码中,obj 是通过 Foo 类实例化的对象。
其实,不仅 obj 是一个对象,Foo类本身也是一个对象。
因为在Python中一切事物都是对象。
按照一切事物都是对象的理论:obj对象是通过执行Foo类的构造方法创建,那么Foo类对象应该也是通过执行某个类的构造方法创建。
print type(f) # 输出: 表示,obj 对象由Foo类创建
print type(Foo) # 输出: 表示,Foo类对象由 type 类创建
所以,obj 对象是Foo类的一个实例,Foo类对象是 type 类的一个实例。
即:Foo类对象是通过type类的构造方法创建。
def func(self):
print("Hello, %s" % self.name)
def __init__(self, name, age):
self.name = name
self.age = age
Foo = type('Foo', (object,), {'__init__': __init__, 'func': func})
f = Foo("Will", 30)
print(type(Foo))
f.func()
类是由type
类实例化产生
那么type类中如何实现的创建类?类又是如何创建对象?
类中有一个属性 metaclass,其用来表示该类由 谁 来实例化创建,所以,我们可以为 metaclass 设置一个type类的派生类,从而查看 类 创建的过程。
class MyType(type):
def __init__(self, child_cls, bases=None, dict=None):
print("--MyType init---", child_cls, bases, dict)
# super(MyType, self).__init__(child_cls, bases, dict)
# def __new__(cls, *args, **kwargs):
# print("in mytype new:",cls,args,kwargs)
# type.__new__(cls)
def __call__(self, *args, **kwargs):
print("in mytype call:", self, args, kwargs)
obj = self.__new__(self, args, kwargs)
self.__init__(obj, *args, **kwargs)
class Foo(object, metaclass=MyType): # in python3
# __metaclass__ = MyType # in python2
def __init__(self, name):
self.name = name
print("Foo ---init__")
def __new__(cls, *args, **kwargs):
print("Foo --new--")
return object.__new__(cls) # 继承父类的__new__方法
def __call__(self, *args, **kwargs):
print("Foo --call--", args, kwargs)
# 第一阶段:解释器从上到下执行代码创建Foo类
# 第二阶段:通过Foo类创建obj对象
obj = Foo("Alex")
# print(obj.name)
类的生成 调用 顺序依次是 __new__
--> __call__
--> __init__
三、异常处理
参考
3.1 异常基础
在编程过程中为了增加友好性,在程序出现bug时一般不会将错误信息显示给用户,而是现实一个提示的页面,通俗来说就是不让用户看见大黄页!
li = []
try:
li[1]
except IndexError as e:
print("Index error: ", e)
3.2 异常种类
3.2.1 常用异常
AttributeError
试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x
IOError
输入/输出异常;基本上是无法打开文件
ImportError
无法引入模块或包;基本上是路径问题或名称错误
IndentationError
语法错误(的子类) ;代码没有正确对齐
IndexError
下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]
KeyError
试图访问字典里不存在的键
KeyboardInterrupt
Ctrl+C被按下
NameError
使用一个还未被赋予对象的变量
SyntaxError
语法错误
TypeError
传入对象类型与要求的不符合
UnboundLocalError
试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,导致你以为正在访问它
ValueError
传入一个调用者不期望的值,即使值的类型是正确的
3.2.2 更多异常
ArithmeticError | AssertionError | AttributeError | BaseException |
---|---|---|---|
BufferError | BytesWarning | DeprecationWarning | EnvironmentError |
EOFError | Exception | FloatingPointError | FutureWarning |
GeneratorExit | ImportError | ImportWarning | IndentationError |
IndexError | IOError | KeyboardInterrupt | KeyError |
LookupError | MemoryError | NameError | NotImplementedError |
OSError | OverflowError | PendingDeprecationWarning | ReferenceError |
RuntimeError | RuntimeWarning | StandardError | StopIteration |
SyntaxError | SyntaxWarning | SystemError | SystemExit |
TabError | TypeError | UnboundLocalError | UnicodeDecodeError |
UnicodeEncodeError | UnicodeError | UnicodeTranslateError | UnicodeWarning |
UserWarning | ValueError | Warning | ZeroDivisionError |
3.3 异常实例
3.3.1 单个异常处理
实例:IndexError
dic = ["wupeiqi", 'alex']
try:
dic[10]
except IndexError as e:
print('错误', e)
实例:KeyError
dic = {'k1':'v1'}
try:
dic['k20']
except KeyError as e:
print('错误', e)
实例:ValueError
s1 = 'hello'
try:
int(s1)
except ValueError as e:
print('错误', e)
3.3.2 多个异常处理
# 未捕获到异常,程序直接报错
s1 = 'hello'
try:
int(s1)
except IndexError as e:
print('错误', e)
上述实例中,异常类只能用来处理指定的异常情况,如果非指定异常则无法处理。
所以,写程序时需要考虑到try代码块中可能出现的任意异常,可以这样写:
s1 = 'hello'
try:
int(s1)
except IndexError as e:
print('错误', e)
except KeyError as e:
print('错误', e)
except ValueError as e:
print('错误', e)
也可以合并,写为以下形式:
s1 = 'hello'
try:
int(s1)
except (IndexError, KeyError, ValueError) as e:
print('错误', e)
但这样写会分不清错误类型。除非出错类型的处理方式相同,不然不要这样写。
3.3.3 万能异常
在Python的异常中,有一个万能异常 Exception
,可以捕获任意异常。
s1 = 'hello'
try:
int(s1)
except Exception as e:
print('未知错误', e)
但有了这个万能异常,其他异常就不是可以忽略了。
对于特殊处理或提醒的异常需要先定义,最后定义Exception
来确保程序正常运行。
s1 = 'hello'
try:
int(s1)
except KeyError as e:
print('键错误', e)
except IndexError as e:
print('索引错误', e)
except Exception as e:
print('未知错误', e)
3.4 异常的其他结构
num = 1
try:
# 主代码块
print(num)
except Exception as e:
# 异常时,执行该块
print('未知错误', e)
else:
# 主代码块执行完,执行该块
print("It's work.")
finally:
# 无论异常与否,最终执行该块
print("No matter it's error or not, I'll run it.")
3.5 主动触发异常
try:
raise Exception('错误了。。。')
except Exception as e:
print(e)
3.6 自定义异常
class OwnException(Exception):
def __init__(self, msg):
self.message = msg
def __str__(self):
return self.message
try:
raise OwnException("Cannot connect the database")
except OwnException as e:
print(e)
四、Socket编程
参考
4.1
Socket Families 地址簇
- socket.AF_INET IPv4(默认)
- socket.AF_INET6 IPv6
- socket.AF_UNIX 只能够用于单一的Unix系统进程间通信
Socket Types 类型
- socket.SOCK_STREAM 流式socket , for TCP (默认)
- socket.SOCK_DGRAM 数据报式socket , for UDP
- socket.SOCK_RAW 原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
- socket.SOCK_RDM 是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。
- socket.SOCK_SEQPACKET 可靠的连续数据包服务
Socket Protocol 协议
五、作业:简单FTP
开发简单的FTP:
- 用户登陆
- 上传/下载文件
- 不同用户家目录不同
- 查看当前目录下文件
- 充分使用面向对象知识