目录
- 面向过程和面向函数编程
- 面向对象中的类和对象
- 面向函数编程与面向对象编程
- 如何编写面向对象程序
- 面向对象三大特性之封装
- 面向对象三大特性之继承
- 面向对象三大特性之多态
- 面向对象之成员变量
- 面向对象之私有变量
- 面型对象之成员方法
- 面型对象成员之私有方法
- 面向成员之属性
- 面向对象综合练习
- 面向对象之组合
- 主动调用其他类的成员
- 面向对象中的特殊成员
- isinstance、type、issubclass
- 区分函数和方法
- 初识反射
- 反射在面向对象中的应用
- 类的约束
- 抽象类与抽象方法实现约束
- 自定义异常类
- md5加密
- md5加密应用
- 异常处理
- 面向对象多继承之c3算法
- 面向对象多继承补充
- 网络编程之bs和cs架构
- 网络基础及socket基本使用
- 网络编程之socket流程解析
- socket自动回复案例
面向过程和面向函数编程
面向过程编程:
# coding:utf-8
'''
面向过程实现:计算字符串与列表中元素的个数
'''
count_str = 0
str = 'Thanlon'
for i in str:
count_str += 1
count_list = 0
lst = ['I', 'am', 'Thanlon']
for j in lst:
count_list += 1
print(count_str, count_list)
'''
7 3
'''
面向函数编程:
# coding:utf-8
'''
面向函数实现计算字符串与列表中元素的个数
'''
def count(param):
count = 0
for i in param:
count += 1
return count
str = 'Thanlon'
lst = ['I', 'am', 'Thanlon']
print(count(str), count(lst))
'''
7 3
'''
由此可见,相对于面向过程编程,面向函数的编程可以减少重复的代码、增强代码的可读性。功能越多的情况下,面向对象的优势就会越明显。
面向对象中的类和对象
- 什么是类
就是具有相同属性和功能的一类事物 - 什么是对象
就是类的具体表现形式
面向函数编程与面向对象编程
面向函数:
# coding:utf-8
'''
用户认证相关
'''
def login():
pass
def regisgter():
pass
# 统计相关
def count(param):
count = 0
for i in param:
count += 1
return count
面向对象:
# coding:utf-8
'''
用户认证相关
'''
class Foo(object):
def login(self):
pass
def regisgter(self):
pass
# 统计相关
class Foo2(object):
def count(self, param):
count = 0
for i in param:
count += 1
return count
对比两种方式,不难发现:面向对象把相似的功能函数进行了划分,代码就显得更加清晰明了。
- 面向对象编程的优点
面向对象编程是一类相似功能函数的集合,使代码更清晰化,更合理化;
面向对象中的类就是一个公共模板,对象就是从具体的模板实例化出来的
如何编写面向对象程序
- 面向对象基本格式
# 定义类
class 类名: # 首字母大写
def 方法名(self, name):
print(name)
return name
def 方法名(self, name):
print(name)
return name
……
# 调用类中方法
# 1.创建该类的对象
obj = 类名()
# 2.通过对象调用方法
result = obj.方法名('Thanlon')
print(result)
- 面向对象的应用场景(什么时候使用面向对象)
调用很多函数,需要给函数进行归类和划分。对于函数特别少、功能特别少,用函数就能实现,不用面向对象。 - 对象的作用
存储一些值,以后方便自己使用。
面向对象三大特性之封装
- 封装的好处
把函数封装在类中,把数据封装到对象,方便使用。 - 封装的形式
一种是将函数封装在类中,一种是把一些值封装到对象中 - 封装示例
class Person:
def show(self):
tmp = "My name is %s,my age is %s,my gender is %s" % (self.name, self.age, self.gender)
print(tmp)
p1 = Person()
p1.name = 'Thanlon'
p1.gender = '男'
p1.age = 23
p1.show()
p2 = Person()
p2.name = 'KiKu'
p2.gender = '女'
p2.age = 25
p2.show()
- 对封装示例改进
class Person:
def __init__(self):# 初始化方法,说构造方法是不准确的
self.name = 'Thanlon'
self.gender = '男'
self.age = 23
def show(self):
tmp = "My name is %s,my age is %s,my gender is %s" % (self.name, self.age, self.gender)
print(tmp)
# 类()实例化对象,自动执行此类的__init__方法
p1 = Person()
print(p1.name, p1.gender, p1.age)
p1.show()
p2 = Person()
p2.show()
- 为了能让不同对象封装不一样的值,需要对封装示例进一步改进
class Person:
def __init__(self, name, gender, age): # init方法是可以加参数的
self.name = name
self.gender = gender
self.age = age
def show(self):
tmp = "My name is %s,my age is %s,my gender is %s" % (self.name, self.age, self.gender)
print(tmp)
# 类()实例化对象,自动执行此类的__init__方法
p1 = Person('Thanlon', '男', '23')
p1.show()
p2 = Person('KiKu', '女', '25')
面向对象三大特性之继承
- 什么是面向对象的继承
继承是面向对象软件技术当中的一个概念。如果一个类别A“继承”另一个类别B,就把这个A称为B的“子类别”,而把B称为“A的父类别”也可以称“B是A的超类”。继承可以使得子类别具有父类别的各种属性和方法,而不需要再次编写相同的代码。在令子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得与父类别不同的功能。一般静态的面向对象编程语言,继承属于静态的,意即在子类别的行为在编译期就已经决定,无法在执行期扩充。 - 为什么要使用继承
实现代码的复用,增加了类的耦合性,使得代码更加规范化、合理化。 - 继承的分类
单继承和多继承 - 单继承
子类可以只继承一个(父)类。子类的实例化对象调用类中的变量或方法的顺序是:先调用子类中的再调用父类中的。
class Superbase:
def fun01(self):
pass
class Base(Superbase): # 父类/基类
def fun02(self):
pass
class Foo(Base): # 字类/派生类
def func03(self):
pass
foo = Foo()
foo.func03()
foo.fun02()
foo.fun01()
- 多继承
子类可以同时继承多个(父)类。如果继承的这些类中有相同的方法或变量,当子类不存在此方法或变量,而需要调用父类中的这个方法或变量时,最先继承的类中的方法或变量会先被调用。如class Foo(Base01, Base02),先调用类Base01中的方法。
class Base01(): # 父类/基类
def show(self):
print('Base01.show()')
class Base02(): # 父类/基类
def show(self):
print('Base-2.show()')
class Foo(Base01, Base02): # 字类/派生类
def func03(self):
pass
foo = Foo()
foo.show() # Base01.show()
- 继承练习1
注意:self是哪个类的对象,就从哪个类开始找(自己如果没有就去找父类)
class Base:
def f1(self):
print('Base.f1')
def f3(self):
self.f1()
print('Base.f3')
class Foo(Base):
def f1(self):
print('Foo.f1')
def f2(self):
print('Foo.f2')
self.f3()
obj = Foo()
obj.f2()
# Foo.f2
# Foo.f1
# Base.f3
obj2 = Base()
obj2.f3()
# Base.f1
# Base.f3
- 继承练习2
注意:继承的父类的先后顺序
class Base01:
def f1(self):
print('Base01.f1')
class Base02:
def f1(self):
print('Base02.f2')
def f3(self):
print('Base02.f3')
self.f1()
class Foo(Base01, Base02):
def f0(self):
print('Foo.f0')
self.f3()
obj = Foo()
obj.f0()
# Foo.f0
# Base02.f3
# Base01.f1
面向对象三大特性之多态
python中多态无处不在,同一个变量可以有多种状态,同一个对象也可以有多种状态。在java和CSharp语言中,定义变量和给变量赋值就必须定义数据类型,而类似于python这种弱定义类的语言,变量可以是任意形态。比如,创建一个变量tmp,给tmp赋值10,那么tmp就是整型的。再将tmp赋值'thanlon',那么它就是字符串类型。这就体现出多态性,同一个变量可以是多种形态。
面向对象之成员变量
- 成员变量分类
类变量与实例变量 - 示例程序
class Foo:
# 定义类变量(静态字段)
sex = 'man'
def __init__(self, name):
# 定义实例变量(字段)
self.name = name # 实例变量/字段
# 对象obj1和对象obj2各自维护自己的实例变量,修改obj1的实例变量,不影响obj2的实例变量值
# obj1 = Foo('Thanlon') # Foo的对象/Foo类的实例
# obj2 = Foo('Kiku')
# obj1.name = 'Thanlon Smith'
# print(obj1.name) # Thanlon Smith
# print(obj2.name) #Kiku
# 可以使用对象调用类变量,但是不能修改类变量的值
obj1 = Foo('Thanlon')
obj2 = Foo('Kiku')
print(obj1.sex) # man
print(obj2.sex) # man
obj1.sex = 'woman' # obj1定义了自己的变量sex,obj1.sex访问sex时,先访问自己中的变量sex
print(obj1.sex) # 'woman''
print(obj2.sex) # man
print(Foo.sex) # man
- 类变量
类变量,也称为静态字段。类变量是在类中定义的变量,如示例程序中的sex = 'man'。类变量(静态字段)的访问,可以使用类访问,也可以使用对象访问。单优先使用类访问,实在不方便,才使用对象访问。 - 实例变量
实例变量,也称为字段。实例变量是在 _ _ init _ _方法中定义的变量,如示例程序中的self.name = name,实例变量(字段)访问时使用是对象访问。
面向对象之私有变量
- 私有变量分类
私有类变量、私有实例变量 - 私有类变量
内部可以访问私有类变量(self.私有变量名、类名.私有变量名),外部不可以访问私有变量,但可以借助类中的方法间接访问私有变量。
class Foo:
__age = 12
def f1(self):
# 内部调用
print(self.__age)
print(Foo.__age) # 推荐
obj = Foo()
# 外部无法调用私有类变量
# print(Foo.__age)
# print(obj.__age)
# 外部通过类中的方法间接访问私有类变量
obj.f1()
其实外部还可以强制访问私有类变量:
class Foo:
__age = 12
obj = Foo()
# 外部强制访问
print(obj._Foo__age)
- 私有实例变量
内部可以访问私有实例变量,外部不可以访问私有变量,但可以借助类中的方法间接访问私有变量。
class Foo:
def __init__(self, name):
self.__name = name
def f1(self):
# 内部可以访问__name
print(self.__name)
obj = Foo('Thanlon')
# obj.__name # 访问失败
obj.f1() # 间接可以访问
面型对象之成员方法
- 实例方法
需要使用对象中封装的变量,如name,就可以使用实例方法
class Foo:
def __init__(self, name):
self.name = name
# 实例化方法(一般方法)
def f1(self):
print(self.name)
obj = Foo('Thanlon')
obj.f1()
- 静态方法
如果方法无需使用对象中封装的值,那么就可以使用静态方法。写静态方法时,方法上方需要写@staticmethod,方法中参数可有可无,参数中不可以用self会出错,解释器执行时也不会将self自动传入参数列表。
class Foo:
def __init__(self, name):
self.name = name
# 静态方法,如果方法无需使用对象中封装的值,那么就可以使用静态方法
@staticmethod
def f2():
print('静态方法') # 没有使用对象封装的值
Foo.f2() # 可以通过类调用静态方法
obj = Foo('Thanlon')
obj.f2() # 也可以通过对象调用方法
静态方法调用时,可以使用类.方法名,也可以使用对象.方法名,但推荐类来调用(调用字段的时候也是)。
- 类方法
如果在方法中会使用到当前类,那么就可以使用类方法。定义类方法时,方法上方写@classmethod,方法中至少有一个参数cls。
class Foo:
def __init__(self, name):
self.name = name
@classmethod
def show(cls): # 和实例方法一样至少有一个参数,自动传递当前类
print(cls)
Foo.show()
f = Foo('')
f.show()
'''
类方法! <class '__main__.Foo'>
'''
类方法和静态方法一样,可以使用类.方法名,也可以使用对象.方法名,但推荐类来调用。
面型对象成员之私有方法
- 私有实例方法
对于私有实例方法可以通过非私有实例方法可以间接访问私有实例方法。
# 私有实例方法
class Foo:
def __init__(self, name):
self.name = name
def __f(self):
print(self.name)
def getF(self):
self.__f()
f = Foo('Thanlon')
# f.__f() # 调用失败
f.getF()
'''
Thanlon
'''
- 私有静态方法
对于私有静态方法,我们可以通过非私有实例方法可以间接访问私有静态实例方法。
# 私有静态方法
class Foo:
def __init__(self, name):
self.name = name
@staticmethod
def __f():
print('私有静态方法!')
def get_f(self):
self.__f()
# Foo.__f()
# 类.静态方法调用失败
# Foo.__f()
# 通过非私有静态方法
obj = Foo('Thanlon')
obj.get_f()
'''
私有静态方法!
'''
通过非私有静态方法也可以间接访问私有静态实例方法:
# 私有静态方法
class Foo:
def __init__(self, name):
self.name = name
@staticmethod
def __f():
print('通过非私有静态方法调用私有静态实例方法!')
@staticmethod
def get_f():
Foo.__f()
# 类.静态方法调用失败
# Foo.__f()
# 类通过调用非私有静态方法间接调用私有静态实例方法!
Foo.get_f()
# 对象通过调用非私有静态方法间接调用私有静态实例方法
obj = Foo('Thanlon')
obj.get_f()
'''
私有静态方法!
'''
- 私有类方法
通过非私有实例方法可以间接访问私有类方法。
# 私有类方法
class Foo:
def __init__(self, name):
self.name = name
@classmethod
def __f(cls):
print(cls)
def get_f(self):
self.__f()
# Foo.__f()
obj = Foo('Thanlon')
obj.get_f()
'''
<class '__main__.Foo'>
'''
通过非私有静态方法也可以间接访问私有类方法:
# 私有类方法
class Foo:
def __init__(self, name):
self.name = name
# 私有静态方法
@classmethod
def __f(cls):
print(cls)
def get_f(self):
self.__f()
# Foo.__f()
# 静态方法
@staticmethod
def reget_f():
Foo.__f()
# 类调用静态方法reget_f
Foo.reget_f()
# 对象调用静态方法reget_f
obj = Foo('Thanlon')
obj.get_f()
'''
<class '__main__.Foo'>
<class '__main__.Foo'>
'''
面向成员之属性
- 什么是属性
属性是通过方法改造胡的,属性代码编写时需要方法的上面加上@property,方法的参数只有一个self。属性在调用时,无需加括号,使用对象.方法。属性的应用场景是对于简单的方法,当不需要传参且有返回值时,可以使用。 - 属性与私有属性
class Foo:
def __init__(self):
pass
@property
def start(self):
return 'start'
@property
def stop(self):
return 'stop'
f_obj = Foo()
print(f_obj.start, f_obj.stop)
'''
start stop
'''
当然,属性有公有和私有之分,私有属性的定义可以在方法的前面加上双下划线。私有属性可以通过使用类中的其它方法访问:
class Foo:
def __init__(self):
pass
# 私有属性__start
@property
def __start(self):
return 'start!'
# 私有属性__stop
@property
def __stop(self):
return 'stop!'
# 通过方法访问私有属性
def get_start_stop(self):
print(self.__start)
print(self.__stop)
f_obj = Foo()
f_obj.get_start_stop()
'''
start!
stop!
'''
面向对象综合练习
- 实现分页
# coding:utf-8
class Pagination:
'''
处理分页的类
'''
def __init__(self, lst, page_num, per_page_num=10):
'''
initialize
:param lst:所有数据
:param page_num:查看的页码
:param per_page_num:每页显示的数据记录数
'''
self.lst = lst
self.page_num = page_num
self.per_page_num = per_page_num
@property
def start(self):
'''
计算索引的起始位置
:return:self.per_page_num * (self.page_num - 1)
'''
return self.per_page_num * (self.page_num - 1)
@property
def end(self):
'''
计算索引的结束位置
:return:self.per_page_num * self.page_num
'''
return self.per_page_num * self.page_num
def show(self):
print(self.lst[self.start:self.end])
lst = []
for items in range(0, 1000):
lst.append(items)
while True:
# 查看的页码
page_num = int(input('请输入页码:'))
# 每页显示10条数据
obj = Pagination(lst, page_num)
obj.show()
请输入页码:1
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
请输入页码:2
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
请输入页码:3
[20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
请输入页码:
面向对象之组合
组合:将一个类的对象封装到另一个类的对象的属性中,就叫组合。
# 将教师类对象封装到学校类对象的属性中
class School(object):
def __init__(self, sch_name, sch_addr, sch_postcode):
self.sch_name = sch_name
self.sch_addr = sch_addr
self.sch_postcode = sch_postcode
def teach_stu(self):
pass
class Teacher(object):
def __init__(self, name, age, salary):
self.name = name
self.age = age
self.__salary = salary
self.school = None
# 实例化School
school1 = School('华东理工大学', '中国上海', '200237')
school2 = School('华东师范大学', '中国上海', '200062')
# 实例化Teacher
teacher1 = Teacher('Thanlon', 23, 200000)
teacher2 = Teacher('Kiku', 25, 500000)
#为教师分配校区
teacher1.school = school1
teacher2.school = school2
#查看教师所在学校的相关信息
print(teacher1.school.sch_name)
print(teacher1.school.sch_addr)
print(teacher1.school.teach_stu())
print(teacher2.school.sch_name)
print(teacher2.school.sch_addr)
print(teacher2.school.teach_stu())
'''
华东理工大学
中国上海
None
华东师范大学
中国上海
None
'''
主动调用其他类的成员
- 主动调用其它类的成员方式一
class Base(object):
def f1(self):
print('Base.f1()')
pass
class Foo(object):
def f1(self):
Base.f1(self)
obj = Foo()
obj.f1()
通过对象调用实例方法:
obj = Base()
obj.f1()
等价于用类这样调用实例方法(不常用):
obj = Base()
Base.f1(obj)
总结:实例方法的调用可以不通过对象去调用,可以通过类调用实例方法,但是需要手动传入对象,
Base.实例方法(自己传入self)
- 主动调用其它类的成员方式二
# coding:utf-8
class Base(object):
def f1(self):
print('Base.f1()')
pass
class Foo(Base):
def f1(self):
super().f1()
obj = Foo()
obj.f1()
'''
Base.f1()
'''
# coding:utf-8
class Base(object):
def f1(self):
print('Base.f1()')
pass
class Foo1(object):
def f1(self):
super().f1()
class Foo2(object):
def f1(self):
print('Foo2.f1()')
class Info(Foo1, Foo2):
pass
obj = Info()
obj.f1()
'''
先找Info类中有无f1方法,Info类中没有按照继承顺序,找下一个类Foo1。
Foo1中找到f1方法,即Info类执行Foo1中的f1方法。
Foo1中的f1方法执行super().f1(),super().f1()即按照Info类的继承顺序找下一个类,
执行Foo2类中的f1方法
'''
面向对象中的特殊成员
- _ _ init _ _
类名():自动执行该方法
# coding:utf-8
class Foo(object):
def __init__(self):
print('__init__')
f = Foo()
'''
__init__
'''
- _ _ call _ _
对象():自动执行 _ _ call _ _ 方法
# coding:utf-8
class Foo(object):
def __init__(self, name):
self.name = name
def __call__(self, *args, **kwargs):
print(args, kwargs)
f = Foo('Thanlon')
# 对象()自动执行__call__方法:无返回值,返回None
f(1, 2, 3, k1=123)
- _ _ getitem _ _
对象[]:自动执行_ _ getitem _ _ 方法
# coding:utf-8
class Foo(object):
def __init__(self, name):
self.name = name
def __getitem__(self, item):
print(item)
return 'kiku'
f = Foo('Thanlon')
# 对象[]自动执行_ _getitem_ _ 方法
print(f['Love']) #
'''
Love
kiku
'''
- _ _ setitem_ _
对象['xxx'] = xx:自动执行_ _ getitem _ _方法
# coding:utf-8
class Foo(object):
def __init__(self, name):
self.name = name
def __setitem__(self, key, value):
print(key, value)
f = Foo('Thanlon')
f['age'] = 23 # 这种方式的写法是没有返回值的
'''
age 23
'''
age传给key,23传给value,如:f['age'] = 23
- _ _ delitem_ _
del 对象[xxx]:自动执行_ _ delitem_ _方法
# coding:utf-8
class Foo(object):
def __init__(self, name):
self.name = name
def __delitem__(self, key):
print(key)
f = Foo('Thanlon')
del f['Thanlon'] # # 这种方式的写法是没有返回值的
'''
Thanlon
'''
- _ _ add _ _
对象+对象:自动执行_ _ add_ _方法
# coding:utf-8
class Foo(object):
def __init__(self, x):
self.x = x
def __add__(self, other):
return (self.x + other.x) # f1.x+f2.x
f1 = Foo(1)
f2 = Foo(2)
print(f1 + f2)
'''
3
'''
- _ _ enter _ _ 与_ _ exit _ _
with 对象:自动执行 _ _ enter _ _ 和 _ _ exit _ _方法
# coding:utf-8
class Foo(object):
def __init__(self, x):
self.x = x
def __enter__(self):
print('__enter__')
def __exit__(self, exc_type, exc_val, exc_tb):
print('__exit__')
f1 = Foo(1)
with f1:
print('with中的code!')
as接收_ _ enter _ _方法的返回值:
# coding:utf-8
class Foo(object):
def __init__(self, x):
self.x = x
def __enter__(self):
print('__enter__')
return 'return value '
def __exit__(self, exc_type, exc_val, exc_tb):
print('__exit__')
f1 = Foo(1)
with f1 as f:
print(f)
print('with中的code!')
'''
__enter__
return value
with中的code!
__exit__
'''
- _ _ new _ _
类():其实先执行_ _ new _ _ 方法,再执行 _ _ init _ _ 方法
# coding:utf-8
class Foo(object):
def __init__(self):
print('__init__')
def __new__(cls, *args, **kwargs):
print('__new__')
f1 = Foo()
'''
_ _new_ _
'''
程序执行了_ _ new_ _ 方法,打印了“_ _ new_ _ ”。但是,没有执行 _ _ new _ _ 方法中的print('init')语句,说明:先执行的是 _ _ new _ _ 方法,后执行 _ _ init _ _ 方法。
# coding:utf-8
class Foo(object):
def __init__(self, name): # 初始化方法
'''
初始化空的对象(__new__方法返回的空对象)或者说为空对象进行数据初始化
:param name:
'''
self.name = name
print('__init__')
def __new__(cls, *args, **kwargs): # 构造方法
'''
创建一个当前类的空对象
:param args:
:param kwargs:
:return:
'''
print('_ _new_ _')
# 所有的对象都是object创建的
return object.__new__(cls) # Python内部创建一个当前类的空对象(该对象的内部是空的)
f1 = Foo('Thanlon') # 这个对象实际上是由以上两个方法创建的
'''
_ _new_ _
__init__
'''
_ _ init _ _ 方法只有在_ _ new _ _方法有返回值且返回值是当前类创建的对象时才能被调用。
特定(相应)的语法会触发(对应)面向对象中的特殊方法。
isinstance、type、issubclass
- isinstance
isinstance函数:检查第一个参数(子类)是否是第二个参数(父类及父类的父类……)的子类。
# coding:utf-8
class Base(object):
pass
class Foo(Base):
pass
class Foo2(Foo):
pass
print(issubclass(Foo, Base))
print(issubclass(Foo2, Base))
'''
True
True
'''
- type
type函数:返回对象的类型,获取对象是由哪个类创建的。
# coding:utf-8
class Base(object):
pass
b = Base()
print(b, type(b))
'''
<__main__.Base object at 0x000002337B1E9630> <class '__main__.Base'>
'''
if type(b) == Base:
print('b是Base类型!')
type类的应用:计算类的数量
# coding:utf-8
'''
计算类的数量
'''
class Foo1(object):
pass
class Foo2(object):
pass
def f(*args):
foo1_count = 0
foo2_count = 0
for item in args:
if type(item) == Foo1:
foo1_count += 1
else:
foo2_count += 1
return foo1_count, foo2_count # 返回一个元组,等价于 return (foo1_count, foo2_count)
print(f(Foo1(), Foo2(), Foo1(), Foo2, Foo1()))
'''
(3, 2)
'''
ret1, ret2 = f(Foo1(), Foo2(), Foo1(), Foo2, Foo1())
print(ret1, ret2)
'''
3 2
'''
- issubclass
isinstance函数:判断第对象是否是某一个指定类或其及父类的实例。
# coding:utf-8
'''
isinstance函数判断第一个参数(对象)是否是第二个参数(类及所有父类)的实例
'''
class Base(object):
pass
class Foo(Base):
pass
foo = Foo()
print(isinstance(foo, Foo))
print(isinstance(foo, Base))
'''
True
True
'''
b = Base()
print(isinstance(b, Base))
print(isinstance(b, Foo)) # 对象b不是Foo类的实例
'''
True
False
'''
区分函数和方法
一般我们认为写在类中的是方法,写在外面的是函数。但这样说并不准确,下面将深入探讨方法和函数的区分:
在外部定义的是函数:
def func():
pass
print(func)
'''
<function func at 0x000001AC2340C1E0>
'''
在类内部定义的可以是方法:
# coding:utf-8
class Foo(object):
def f(self):
pass
obj = Foo()
print(obj.f)
'''
<bound method Foo.f of <__main__.Foo object at 0x00000254215AB1D0>>
'''
类调用静态方法,会把静态方法当作函数。所以,在类中定义的不一定是方法,还可以是函数:
# coding:utf-8
'''
定义在类中的还可以是函数
'''
class Foo(object):
def f(self):
pass
@staticmethod
def f2():
pass
obj = Foo()
print(Foo.f2) # 函数
print(obj.f2) # 函数
'''
<function Foo.f2 at 0x000001AF9471C7B8>
<bound method Foo.f of <__main__.Foo object at 0x000001AF9467B198>>
'''
再次探讨:使用类调用实例方法,会把实例方法当作函数,需要手动传self。所以,类中定义的也不一定是方法:
# coding:utf-8
class Foo(object):
def f1(self):
pass
@staticmethod
def f2():
pass
obj = Foo()
Foo.f1(obj) # obj需要自己传参
print(Foo.f1) # 把f1当作函数
obj = Foo()
obj.f1() # 不需要自己传参
print(obj.f1) # 把f1当作方法
'''
<function Foo.f1 at 0x000001F67119C730>
<bound method Foo.f1 of <__main__.Foo object at 0x000001F671193588>>
'''
准确判断是方法还是函数?
# coding:utf-8
from types import MethodType, FunctionType
def check(param):
if isinstance(param, MethodType):
print(param, 'is a method!')
elif isinstance(param, FunctionType):
print(param, 'is a function!')
else:
print('Others!')
class Foo(object):
def f1(self):
pass
@staticmethod
def f2():
pass
obj = Foo()
check(obj.f1) # 方法
check(Foo.f2) # 函数
check(obj.f2) # 函数
'''
<bound method Foo.f1 of <__main__.Foo object at 0x000001F62C49C198>> is a method!
<function Foo.f2 at 0x000001F62C4A1510> is a function!
<function Foo.f2 at 0x000001F62C4A1510> is a function!
'''
新的发现,对象调用的是方法,类调用的是函数,且函数需要手动传参:
# coding:utf-8
class Foo(object):
def f1(self):
pass
def f2(self):
pass
def f3(self):
pass
lst = [f1, f2]
obj = Foo()
obj.lst.append(obj.f3)
for item in Foo.lst:
print(item)
'''
<function Foo.f1 at 0x000001E53675C730>
<function Foo.f2 at 0x000001E53675C7B8>
<bound method Foo.f3 of <__main__.Foo object at 0x000001E536753588>>
'''
总结:一般我们认为在类中是方法,写在外面的是函数。其实,这样判断是函数还是方法是不精准的。方法和函数的区分,不仅和定义的位置有关系,还和方法或函数的调用者有关系。如果通过对象.xxx调用,那么xxx就是方法;如果通过类.xxx调用或者直接执行xxx,那么xxx就是个函数。面向对象中,方法中self不需要自己传参,而函数中每个参数都需要自己传参。
初识反射
- 什么是反射
python面向对象中的反射就是通过字符串获取模块、对象或类的属性,进行操作。 - 为什么使用反射
有这样的一种场景:用户输入函数序号来执行函数。当没有使用反射时,可以这样设计程序:
# coding:utf-8
def func01():
print('func01')
def func02():
print('func02')
def func03():
print('func03')
# coding:utf-8
import deal
while True:
print('''
系统支持的函数:
1、f1
2、f2
3、f3
4、f4
''')
func_name = input(' 请输入您要执行的函数序号:')
if func_name == '1':
deal.func01()
elif func_name == '2':
deal.func02()
elif func_name == '3':
deal.func03()
如果在函数很多的情况下,就需要写很多判断条件。下面你来看使用反射实现相同功能的程序代码:
# coding:utf-8
import deal
from types import FunctionType
while True:
print('''
系统支持的函数:
1、f1
2、f2
3、f3
4、f4
''')
func_name = input(' 请输入您要执行的函数序号:')
# 使用反射:
if hasattr(deal, func_name):
func_or_value = getattr(deal, func_name) # func_name是字符串,根据字符串去模块中找与之同名的成员
if isinstance(func_or_value, FunctionType): # 可能是值,也可能是函数,所以在这里需要去判断
func_or_value()
else:
print(func_or_value)
else:
print('deal模块中不存在输入的函数名!')
如果不断增加函数,不使用反射会写很多判断条件。而使用反射,很明显程序中可以少写很多代码。
反射在面向对象中的应用
- getattr
getattr:根据字符串为参数去对象(对象、类、模块)中寻找与之同名的成员。
根据字符串为参数去模块中寻找与之同名的成员:
# coding:utf-8
name = 'Thanlon'
def f1(arg):
print(arg, 'is good!')
# coding:utf-8
import module
v = getattr(module,'name')
print(v)
func_name = getattr(module,'f1')
func_name('Thanlon')
'''
Thanlon
Thanlon is good!
''
根据字符串为参数去类和对象中寻找与之同名的成员:
# coding:utf-8
class Foo(object):
country = 'China'
def f1(self0):
print('f1')
value_name = getattr(Foo, 'country') # 静态字段
print(value_name)
obj = Foo()
value_name = getattr(obj, 'country')# 实例变量
print(value_name)
func_name = getattr(Foo, 'f1') # 函数
print(func_name)
func_name = getattr(obj, 'f1') # 方法
print(func_name)
'''
China
China
<function Foo.f1 at 0x000001EAF1D9D7B8>
<bound method Foo.f1 of <__main__.Foo object at 0x000001EAF1DAD240>>
'''
练习:在用户对象中找到登录和注册方法
# coding:utf-8
class User(object):
func_lst = ['register', 'login']
def register(self):
print('register')
def login(self):
print('login')
def run(self):
print('''
系统支持的函数:
1、注册
2、登录
''')
choice = int(input('请输入要执行序号(序号对应相应的方法):'))
func_name = User.func_lst[choice - 1] # 类变量优先通过类去调用,这里用User;当然也可以用对象self
# func = getattr(User, func_name) # User.register、User.login;类.函数
# func(self)
func = getattr(self, func_name) # self.register、self.login;对象.方法
func()
obj = User()
obj.run()
obj2 = User()
obj2.run()
- hasattr
hasattr:根据字符串形式,去判断对象中是否有成员
# coding:utf-8
import module
v_ret = hasattr(module,'name')
func_ret = hasattr(module,'f1')
print(v_ret,func_ret)
'''
True True
'''
- setattr
setattr:根据字符串形式,动态去设置一个成员(内存)
# coding:utf-8
import module
setattr(module,'lover','Kiku')
v = getattr(module,'lover')
print(v)
setattr(module,'f2',lambda x:x*2)
func_name = getattr(module,'f2')
print(func_name)
print(func_name(1996))
'''
Kiku
<function <lambda> at 0x0000020189A3C1E0>
3992
'''
注意:如果设置属性,为让大家明白,需要在清楚写明类中有哪些设置的成员,如在 _ _ init _ _方中self.age = None。不建议使用setattr方法。
# coding:utf-8
import module
class Foo(object):
def __init__(self, name):
self.name = name
self.age = None # 等待setattr
obj = Foo('Thanlon')
setattr(obj, 'age', '23')
setattr(Foo, 'age', '23')
print(getattr(Foo,'age'))
- delattr
delattr:根据字符串形式,动态删除一个成员(内存)
# coding:utf-8
import module
setattr(module,'lover','Kiku')
v = getattr(module,'lover')
print(v)
setattr(module,'f2',lambda x:x*2)
func_name = getattr(module,'f2')
print(func_name)
print(func_name(1996))
delattr(module,'lover')
v = getattr(module,'lover')
delattr(module,'f2')
func_name = getattr(module,'f2')
类的约束
- 约束的实现
Foo类继承了父类Base,Base中存在f方法,f方法中抛出一个NotImplementedError的异常。在Foo中没有重写Base中的f方法时,调用f方法,抛出异常,表示Foo方法中必须重写f方法。通过这种做法,强制类中必须重写父类中的方法,进而可以实现约束。
# coding:utf-8
class Base(object):
def f(self):
raise NotImplementedError('f方法必须被重写!')
class Foo(Base):
pass
obj = Foo()
obj.f()
'''
NotImplementedError: f方法必须被重写!
'''
重写父类中的f方法自然就没有异常信息。
# coding:utf-8
class Base(object):
def f(self):
raise NotImplementedError('f方法必须被重写!')
class Foo(Base):
def f(self):
pass
obj = Foo()
obj.f()
- 约束的应用场景
多个类,内部都必须有某些方法时,需要使用基类+异常进行约束。
抽象类与抽象方法实现约束
定义抽象类和抽象方法,如果某类继承了抽象类,必须重写抽象方法,否则会报错。
from abc import ABCMeta, abstractmethod
class Base(object, metaclass=ABCMeta): # 定义一个抽象类
def f1(self):
print('f1')
@abstractmethod
def f2(self):
'''
抽象方法
:return:
'''
pass
class Foo(Base):
pass
obj = Foo()
'''
如果没有重写抽象方法,不能实例化类
Can't instantiate abstract class Foo with abstract methods f2
'''
需要重写了抽象类的f2方法:
from abc import ABCMeta, abstractmethod
class Base(object, metaclass=ABCMeta): # 定义一个抽象类
def f1(self):
print('f1')
@abstractmethod
def f2(self):
'''
抽象方法
:return:
'''
pass
class Foo(Base):
def f2(self):
pass
obj = Foo()
自定义异常类
md5加密
- MD5简单的加密
import hashlib
# 实例化对象
obj = hashlib.md5()
# 写入要加密的字节,python3中加密的必须是字节
obj.update('123456'.encode('utf-8'))
# 获取密文
ciphers = obj.hexdigest()
print(ciphers)
'''e10adc3949ba59abbe56e057f20f883e'''
- 加盐
如果撞库(将能想到的常用的密码加密,根据密文找明文),密码可能不安全。使用加盐可以解决撞库问题,增强密码的安全性。
# 使用加盐解决撞库
import hashlib
# 实例化对象
obj = hashlib.md5(b'thanlon') # 加盐
# 写入要加密的字节,python3中加密的必须是字节
obj.update('123456'.encode('utf-8'))
# 获取密文
ciphers = obj.hexdigest()
print(ciphers)
'''1a8c227a607ed3c09a39f4b6a6df8869'''
md5加密应用
- 自定义MD5函数
import hashlib
SALT = b'thanlon'
# 自定义MD5函数
def md5(pwd):
obj = hashlib.md5(SALT)
# pwd是字符串,需要将其转换成字节
obj.update(pwd.encode('utf-8'))
# 返回密文
return obj.hexdigest()
print(md5('123456'))
异常处理
面向对象多继承之c3算法
面向对象多继承补充
网络编程之bs和cs架构
网络基础及socket基本使用
网络编程之socket流程解析
socket自动回复案例
- 服务端只支持一个客户端连接
服务端程序
import socket
# 创建服务端socket对象
server = socket.socket()
# 绑定IP(ipocnfig命令查看ip地址)和端口(自定义网络端口,不和系统已有端口冲突就可以使用)
server.bind(('192.168.5.106', 8000))
# 可以等待5个连接
server.listen(5)
# 等待客户端连接,如果没有连接,则持序等待
# conn:客户端与服务端连接的对象,服务端通过该对象进行收发数据
# addr:客户端地址信息
print('服务端已经运行,等待客户端连接……')
conn, addr = server.accept() # 在这里会阻塞,只有客户端进行连接,才获取客户端连接然后开始进行通行
print('有人来连接了!')
data = conn.recv(1024) # 1024:服务端获取数据一次最多获取1024byte
print('客户端给我发了:', data)
conn.send(b'May I help you?')
conn.close() # 与客户端断开连接
server.close() # 关闭服务端的服务
客户端程序(client.py):
import socket
# 创建客户端socket对象
client = socket.socket()
client.connect(('192.168.5.106', 8000)) # 阻塞,只有连接成功,代码才会继续执行
client.send(b'hello') # 连接服务端后给服务端发送消息
data = client.recv(1024) # 等待服务端发送的消息
print('服务端给我回复了:',data)
client.close() # 关闭连接
服务端运行:
服务端已经运行,等待客户端连接……
有人来连接了!
客户端给我发了: b'hello'
客户端运行:
服务端给我回复了: b'May I help you?'
- 服务端支持多客户端连接
服务端(server.py):
import socket
# 创建服务端socket对象
server = socket.socket()
# 绑定IP和端口
server.bind(('192.168.5.106', 8000))
# 可以等待5个连接
server.listen(5)
# 等待客户端连接,如果没有连接,则持序等待
# conn:客户端与服务端连接的对象,服务端通过该对象进行收发数据
# addr:客户端地址信息
while True:
print('服务端已经运行,等待客户端连接……')
conn, addr = server.accept() # 在这里会阻塞,只有客户端进行连接,才获取客户端连接然后开始进行通行
print('有人来连接了!')
data = conn.recv(1024) # 字节类型
resp = data + ',你好!'.encode('utf-8')
print('已发送“', resp.decode('utf-8'), '”给客户端……')
conn.send(resp) # 字节类型
conn.close()
print('服务端断开连接……', '\n')
客户端程序(client.py):
import socket
# 创建客户端socket对象
client = socket.socket()
client.connect(('192.168.5.106', 8000)) # 阻塞,只有连接成功,代码才会继续执行
name = input('输入你的姓名吧:')
client.send(name.encode('utf-8')) # 将字符串转换为字节,连接服务端后给服务端发送这个字节
resp = client.recv(1024) # 阻塞,等待服务端发送的消息
print(resp.decode('utf-8'))
client.close() # 关闭连接
服务端运行记录:
服务端已经运行,等待客户端连接……
有人来连接了!
已发送“ Thanlon,你好! ”给客户端……
服务端断开连接……
服务端已经运行,等待客户端连接……
有人来连接了!
已发送“ Kiku,你好! ”给客户端……
服务端断开连接……
服务端已经运行,等待客户端连接……
客户端1运行记录:
输入你的姓名吧:Thanlon
Thanlon,你好!
客户端1运行:
客户端2运行记录:
输入你的姓名吧:Kiku
Kiku,你好!
- 在线自动聊天工具
服务端程序(server.py):
import socket
# 创建服务端socket对象
server = socket.socket()
# 绑定IP和端口
server.bind(('192.168.5.106', 8000))
# 可以等待5个连接
server.listen(5)
# 等待客户端连接,如果没有连接,则持序等待
# conn:客户端与服务端连接的对象,服务端通过该对象进行收发数据
# addr:客户端地址信息
while True:
print('\n系统提示:服务端已经运行,等待客户端连接……')
conn, addr = server.accept() # 在这里会阻塞,只有客户端进行连接,才获取客户端连接然后开始进行通行
print('系统提示:' + str(list(addr)) + '客户端已连接本服务端……\n')
while True:
data = conn.recv(1024) # 字节类型
if data == b'0':
break
print('系统提示:已接收客户端发来的信息:“' + data.decode('utf-8') + '”')
resp = data + ',你好!'.encode('utf-8')
print('系统提示:已发送“', resp.decode('utf-8'), '”给客户端……')
conn.send(resp) # 字节类型
print('系统提示:与' + str(list(addr)) + '客户端的连接未关闭,可继续和该客户端进行对话……')
conn.close()
print('系统提示:与' + str(list(addr)) + '客户端的连接已关闭……')
客户端程序(client.py):
import socket
# 创建客户端socket对象
client = socket.socket()
client.connect(('192.168.5.106', 8000)) # 阻塞,只有连接成功,代码才会继续执行
while True:
name = input('系统提示,请输入您的姓名(输入0关闭连接):\n')
client.send(name.encode('utf-8')) # 将字符串转换为字节,连接服务端后给服务端发送这个字节
if name == '0':
break
resp = client.recv(1024) # 阻塞,等待服务端发送的消息
print('系统提示:客户端回复“' + resp.decode('utf-8') + '”')
client.close() # 关闭连接
print('系统提示:关闭客户端连接!')
服务端运行记录:
系统提示:服务端已经运行,等待客户端连接……
系统提示:['192.168.5.106', 57892]客户端已连接本服务端……
系统提示:已接收客户端发来的信息:“Thnlon”
系统提示:已发送“ Thnlon,你好! ”给客户端……
系统提示:与['192.168.5.106', 57892]客户端的连接未关闭,可继续和该客户端进行对话……
系统提示:已接收客户端发来的信息:“Kiku”
系统提示:已发送“ Kiku,你好! ”给客户端……
系统提示:与['192.168.5.106', 57892]客户端的连接未关闭,可继续和该客户端进行对话……
系统提示:与['192.168.5.106', 57892]客户端的连接已关闭……
系统提示:服务端已经运行,等待客户端连接……
系统提示:['192.168.5.106', 57939]客户端已连接本服务端……
系统提示:已接收客户端发来的信息:“Maria”
系统提示:已发送“ Maria,你好! ”给客户端……
系统提示:与['192.168.5.106', 57939]客户端的连接未关闭,可继续和该客户端进行对话……
系统提示:与['192.168.5.106', 57939]客户端的连接已关闭……
系统提示:服务端已经运行,等待客户端连接……
客户1端运行记录:
系统提示,请输入您的姓名(输入0关闭连接):
Thnlon
系统提示:客户端回复“Thnlon,你好!”
系统提示,请输入您的姓名(输入0关闭连接):
Kiku
系统提示:客户端回复“Kiku,你好!”
系统提示,请输入您的姓名(输入0关闭连接):
0
系统提示:关闭客户端连接!
客户2端运行记录:
系统提示,请输入您的姓名(输入0关闭连接):
Maria
系统提示:客户端回复“Maria,你好!”
系统提示,请输入您的姓名(输入0关闭连接):
0
系统提示:关闭客户端连接!