python面向对象

@[toc]

1. 面向过程和面向函数编程
1.1 面向过程编程
# 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
'''
1.2 面向对象中的类和对象

① 什么是类
类是具有相同属性和功能的一类事物。

② 什么是对象
对象是类的具体表现形式。

2. 面向函数编程与面向对象编程
2.1 面向函数
# coding:utf-8
'''
 用户认证相关
'''
def login():
    pass
def regisgter():
    pass
# 统计相关
def count(param):
    count = 0
    for i in param:
        count += 1
    return count
2.2 面向对象
# 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

对比两种方式,不难发现:面向对象把相似的功能函数进行了划分,代码就显得更加清晰明了。

2.3 面向对象编程的优点

面向对象中的类就是一个公共模板,对象就是从具体的模板实例化出来的;面向对象编程是一类相似功能函数的集合,使代码更清晰化,更合理化;

3. 何编写面向对象程序
3.1 面向对象基本格式
# 定义类
class 类名:  # 首字母大写
    def 方法名(self, name):
        print(name)
        return name
    def 方法名(self, name):
        print(name)
        return name
        ……
# 调用类中方法
# 1.创建该类的对象
obj = 类名()
# 2.通过对象调用方法
result = obj.方法名('Thanlon')
print(result)
3.2 面向对象的应用场景(什么时候使用面向对象)

调用很多函数,需要给函数进行归类和划分。对于函数特别少、功能特别少,用函数就能实现,不用面向对象。

3.3 对象的作用

存储一些值,以后方便自己使用。

4. 封装
4.1 封装的优点

把函数封装在类中,把数据封装到对象,方便使用。

4.2 封装的形式

一种是将函数封装在类中,一种是把一些值封装到对象中

4.3 封装示例
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')
5. 继承
5.1 什么是继承

继承是面向对象软件技术当中的一个概念。如果一个类别A“继承”另一个类别B,就把这个A称为B的“子类别”,而把B称为“A的父类别”也可以称“B是A的超类”。继承可以使得子类别具有父类别的各种属性和方法,而不需要再次编写相同的代码。在令子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得与父类别不同的功能。一般静态的面向对象编程语言,继承属于静态的,意即在子类别的行为在编译期就已经决定,无法在执行期扩充。

5.2 为什么要使用继承

实现代码的复用,增加了类的耦合性,使得代码更加规范化、合理化。

5.3 单继承

子类可以只继承一个(父)类。子类的实例化对象调用类中的变量或方法的顺序是:先调用子类中的再调用父类中的。

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()
5.4 多继承

子类可以同时继承多个(父)类。如果继承的这些类中有相同的方法或变量,当子类不存在此方法或变量,而需要调用父类中的这个方法或变量时,最先继承的类中的方法或变量会先被调用。如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()
5.5 继承练习题

① 继承练习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
6. 多态

python中多态无处不在,同一个变量可以有多种状态,同一个对象也可以有多种状态。在java和CSharp语言中,定义变量和给变量赋值就必须定义数据类型,而类似于python这种弱定义类的语言,变量可以是任意形态。比如,创建一个变量tmp,给tmp赋值10,那么tmp就是整型的。再将tmp赋值'thanlon',那么它就是字符串类型。这就体现出多态性,同一个变量可以是多种形态。

7. 成员变量
7.1 成员变量分类

类变量与实例变量

7.2 类变量

类变量,也称为静态字段。类变量是在类中定义的变量,如示例程序中的sex = 'man'。类变量(静态字段)的访问,可以使用类访问,也可以使用对象访问。单优先使用类访问,实在不方便,才使用对象访问。

7.3 实例变量

实例变量,也称为字段。实例变量是在 _ _ init _ _方法中定义的变量,如示例程序中的 self.name = name,实例变量(字段)访问时使用是对象访问。

7.4 成员变量示例程序
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
8. 私有变量
8.1 私有变量分类

私有类变量、私有实例变量

8.2 私有类变量

内部可以访问私有类变量(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)
8.3 私有实例变量

内部可以访问私有实例变量,外部不可以访问私有变量,但可以借助类中的方法间接访问私有变量。

class Foo:
    def __init__(self, name):
        self.__name = name
    def f1(self):
        # 内部可以访问__name
        print(self.__name)
obj = Foo('Thanlon')
# obj.__name  # 访问失败
obj.f1()  # 间接可以访问
9. 成员方法
9.1 实例方法

需要使用对象中封装的变量,如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()
'''
类方法! 
'''

类方法和静态方法一样,可以使用类.方法名,也可以使用对象.方法名,但推荐类来调用。

9.2 私有方法

① 私有实例方法
对于私有实例方法可以通过非私有实例方法可以间接访问私有实例方法。

# 私有实例方法
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 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()

'''


'''
10. 属性
10.1 属性的概念

属性是通过方法改造胡的,属性代码编写时需要方法的上面加上@property,方法的参数只有一个self。属性在调用时,无需加括号,使用对象.方法。属性的应用场景是对于简单的方法,当不需要传参且有返回值时,可以使用。

10.2 属性与私有属性
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!
'''
10.3 练习题

实现分页:

# 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]
请输入页码:

11. 组合

组合:将一个类的对象封装到另一个类的对象的属性中,就叫组合。

# 将教师类对象封装到学校类对象的属性中
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
'''
12. 主动调用其他类的成员

① 方式一

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方法
'''
13. 特殊成员

① _ _ 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_ _
对象[]:自动执行_ _ 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
'''

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 _ _方法有返回值且返回值是当前类创建的对象时才能被调用。
特定(相应)的语法会触发(对应)面向对象中的特殊方法。

14. isinstance、type、issubclass
14.1 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
'''
14.2 type

type函数:返回对象的类型,获取对象是由哪个类创建的。

# coding:utf-8
class Base(object):
    pass

b = Base()
print(b, type(b))
'''
<__main__.Base object at 0x000002337B1E9630> 
'''
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
'''
14.3 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
'''
15. 区分函数和方法

一般我们认为写在类中的是方法,写在外面的是函数。但这样说并不准确,下面将深入探讨方法和函数的区分:
在外部定义的是函数:

def func():
    pass
    
print(func)
'''

'''

在类内部定义的可以是方法:

# coding:utf-8
class Foo(object):
    def f(self):
        pass
        
obj = Foo()
print(obj.f)
'''
>
'''

类调用静态方法,会把静态方法当作函数。所以,在类中定义的不一定是方法,还可以是函数:

# coding:utf-8
'''
定义在类中的还可以是函数
'''
class Foo(object):
    def f(self):
        pass

    @staticmethod
    def f2():
        pass

obj = Foo()
print(Foo.f2)  # 函数
print(obj.f2)  # 函数
'''

>
'''

再次探讨:使用类调用实例方法,会把实例方法当作函数,需要手动传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当作方法
'''

>
'''

准确判断是方法还是函数?

# 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)  # 函数
'''
> is a method!
 is a function!
 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)
'''


>
'''

总结:一般我们认为在类中是方法,写在外面的是函数。其实,这样判断是函数还是方法是不精准的。方法和函数的区分,不仅和定义的位置有关系,还和方法或函数的调用者有关系。如果通过对象.xxx调用,那么xxx就是方法;如果通过类.xxx调用或者直接执行xxx,那么xxx就是个函数。面向对象中,方法中self不需要自己传参,而函数中每个参数都需要自己传参。

16. 反射
16.1 初识反射

① 什么是反射
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模块中不存在输入的函数名!')

如果不断增加函数,不使用反射会写很多判断条件。而使用反射,很明显程序中可以少写很多代码。

16.2 反射在面向对象中的应用

③ 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

>
'''

练习:在用户对象中找到登录和注册方法

# 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
 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')
17. 约束
17.1 约束的实现

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方法自然就没有异常信息。
```python
# coding:utf-8
class Base(object):
    def f(self):
        raise NotImplementedError('f方法必须被重写!')

class Foo(Base):
    def f(self):
        pass

obj = Foo()
obj.f()
17.2 约束的应用场景

多个类,内部都必须有某些方法时,需要使用基类+异常进行约束。

17.3 抽象类与抽象方法实现约束

定义抽象类和抽象方法,如果某类继承了抽象类,必须重写抽象方法,否则会报错

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()

你可能感兴趣的:(python面向对象)