python基础-第七篇-7.2面向对象(进阶篇)

  进入到今天的探索前,我先对上节内容进行一下回顾:

  • 面向对象是一种编程方式,此编程方式的实现是基于对类和对象的使用
  • 类是一个模板,模板中包装了多个函数可供使用
  • 对象是基于类创建的,实例用于调用被包装在类中的函数
  • 对于c#和java--只能用面向对象编程,而对于ruby和python则可以用函数编程和面向对象编程
  • 面向对象不是所有的情况都适用,当两个东西不存在公共的部分,最好用函数编程
  • 对象编程里的self是形式参数,是python内部把对象当参数传递进去的
  • __init__  构造方法--当实例化对象时,会自动执行类里的这个方法,并在此方法里执行具体的封装操作
  • __del__  解释器销毁对象的时候自动调用的方法,叫析构方法

类的成员

  类的成员可以分为三大类:字段、方法和属性

注:所有成员中,只有普通字段的内容保存对象中,即:根据此类创建了多少对象,在内存中就有多少个普通字段。而其他的成员,则都是保存在类中,即:无论对象的多少,在内存中只创建一份。

一、字段

  字段分普通字段和静态字段

  

  另外它们在内存上也有所区别

  • 在前面我们知道,实例化对象的过程其实也是对象带他的普通字段再开辟一份内存空间
  • 静态字段属于类,而普通字段属于对象

  那现在我们来看看静态字段在类调用和对象调用过程中的内存变化--

class schoolmember:

    human_num = 10000

    def __init__(self,name,age,salary):
        self.name = name
        self.age = age
        self.salary = salary

    def sleep(self):
        print(‘%s go to sleep....‘%self.name)

obj1 = schoolmember(‘小明‘,22,10)
obj2 = schoolmember(‘小红‘,56,10)

obj1.salary += 1000
obj2.salary += 2000
print(obj1.salary)  #11000
print(obj2.salary)  #12000
print(‘*‘*20)

obj1.human_num += 3000
obj2.human_num += 4000
print(schoolmember.human_num)  #10000
print(obj1.human_num)  #13000
print(obj2.human_num)  #14000
print(‘*‘*20)

schoolmember.human_num += 5000
print(schoolmember.human_num)  #15000
print(obj1.human_num)  #13000
print(obj2.human_num)  #14000

  我们从内存的角度来看这个问题,当我们使用类名.字段名去调用静态变量的时候,这个变量是存在类的内存中的,这个时候各个对象都可以取到类的内存中的变 量,但是当我们使用对象名.字段名去修改这个静态变量的时候,我们其实是在对象的内存中又开辟了一块空间去存储这个字段和修改后的值,所以一个对象的字段 发生改变,类和其他对象的该字段并不会发生变化。哎呦,总之解决这个问题的最终办法就是始终用 类名.字段名 去使用类中的静态变量就对了!

  

 应用场景: 通过类创建对象时,如果每个对象都具有相同的字段,那么就使用静态字段

二、方法

方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。

  • 普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self;
  • 类方法:由类调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类复制给cls;
  • 静态方法:由类调用;无默认参数,可以不用创建就能实现某个功能,等同在类中实现函数式编程
class Cat:

    def __init__(self,name):
        self.name = name

    #普通方法:至少有一个self参数
    def eat(self):
        print(‘%s is eating...‘%self.name)

    #静态方法,无默认参数
    @staticmethod
    def drink(something,obj):  #可以把对象当做普通参数传入
        print(‘%s is drinking %s.‘%(obj.name,something))

    #类方法,至少有一个cls参数
    @classmethod
    def sleep(cls,obj):
        print(‘%s is sleeping...‘%obj.name)

ca = Cat(‘小花猫‘)
ca.eat()
Cat.drink(‘water‘,ca)
Cat.sleep(ca)

相同点:对于所有的方法而言,均属于类(非对象)中,所以,在内存中也只保存一份。

不同点:方法调用者不同、调用方法时自动传入的参数不同。

三、属性

  特性:普通方法的变种,将方法伪造成一种字段,定义时就self参数,执行的结果为return返回值

# ############### 定义 ###############
class Foo:

    def func(self):
        pass

    # 定义属性
    @property
    def prop(self):
        pass
# ############### 调用 ###############
foo_obj = Foo()

foo_obj.func()
foo_obj.prop   #调用属性

定义和调用

  由属性的定义和调用要注意一下几点:

  • 定义时,在普通方法的基础上添加 @property 装饰器;
  • 定义时,属性仅有一个self参数
  • 调用时,无需括号
               方法:foo_obj.func()
               属性:foo_obj.prop

注意:属性存在意义是:访问属性时可以制造出和访问字段完全相同的假象

属性由方法变种而来,如果Python中没有属性,方法完全可以代替其功能。

实例:对于主机列表页面,每次请求不可能把数据库中的所有内容都显示到页面上,而是通过分页的功能局部显示,所以在向数据库中请求数据时就要显示的指定获取从第m条到第n条的所有数据(即:limit m,n),这个分页的功能包括:

    • 根据用户请求的当前页和总数据条数计算出 m 和 n
    • 根据m 和 n 去数据库中请求数据

# ############### 定义 ###############
class Pager:

    def __init__(self, current_page):
        # 用户当前请求的页码(第一页、第二页...)
        self.current_page = current_page
        # 每页默认显示10条数据
        self.per_items = 10 

    @property
    def start(self):
        val = (self.current_page - 1) * self.per_items
        return val

    @property
    def end(self):
        val = self.current_page * self.per_items
        return val

# ############### 调用 ###############

p = Pager(1)
p.start 就是起始值,即:m
p.end   就是结束值,即:n

从上述可见,Python的属性的功能是:属性内部进行一系列的逻辑计算,最终将计算结果返回。

类成员的修饰符

  刚才我们讲到类的成员大体还可以分为这么两类

  • 公有成员,在任何地方都能访问
  • 私有成员,只有在类的内部才能方法,私有成员命名时,前两个字符是下划线。(特殊成员除外,例如:__init__、__call__、__dict__等)
class Animal:

    __ke = ‘属‘

    def __init__(self,name,sex):
        self.__name = name
        self.__sex = sex

    def get_putongziduan(self):
        print(self.__name,self.__sex)

    def __eat(self):
        print(‘eat‘)

    def __drink(self):
        print(‘drink‘)

    def get_putongfangfa(self):
        self.__drink()
        self.__eat()

    def get_jingtaiziduan(self):
        print(self.__ke)

    def sleep(self):
        print(‘sleep‘)

class Cat(Animal):

    def jiao(self):
        print(‘jiao‘)

    def get_info(self):
        print(self.__name)
        print(self.__ke)
        self.__drink()

#子类创建的对象
cat = Cat(‘小白猫‘,‘male‘)
#父类创建的对象
animal = Animal(‘小花狗‘,‘man‘)

#父类外部调用私有普通字段
# print(animal.__name)
#子类外部外部调用私有普通字段
# print(cat.__name)

#父类外部调用私有方法
# animal.__drink()
#子类外部调用私有方法
# cat.__drink()

#父类内部部调用私有普通字段
animal.get_putongziduan()
cat.get_putongziduan()

#子类内部调用私有字段和私有方法
# cat.get_info()

  结论:私有的只有自己能用,就算你儿子,你孙子来了也用不了

类的特殊成员

  • __doc__  表示类的描述信息

class Foo:
    """ 描述类信息,这是用于看片的神奇 """

    def func(self):
        pass

print Foo.__doc__
#输出:类的描述信息

  • __module__  表示当前操作的对象在哪个模块
  • __class__     表示当前操作的对象的类是什么

#!/usr/bin/env python
# -*- coding:utf-8 -*-

class C:

    def __init__(self):
        self.name = ‘wupeiqi‘

lib.aa.py

from lib.aa import C

obj = C()
print obj.__module__  # 输出 lib.aa,即:输出模块
print obj.__class__      # 输出 lib.aa.C,即:输出类

index.py

  • __init__  构造方法,通过创建对象时,自动执行
  • __del__  析构方法,当对象在内存中被释放时,自动执行

  注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。

  • __call__  对象后面加括号,触发执行   --对象()或类()()

class Foo:

    def __init__(self):
        pass

    def __call__(self, *args, **kwargs):

        print ‘__call__‘

obj = Foo() # 执行 __init__
obj()       # 执行 __call__

  • __dict__  获取类或对象的所有成员(类的普通字段属于对象;类中的静态字段和方法等属于类)

class Province:

    country = ‘China‘

    def __init__(self, name, count):
        self.name = name
        self.count = count

    def func(self, *args, **kwargs):
        print ‘func‘

# 获取类的成员,即:静态字段、方法、
print Province.__dict__
# 输出:{‘country‘: ‘China‘, ‘__module__‘: ‘__main__‘, ‘func‘: <function func at 0x10be30f50>, ‘__init__‘: <function __init__ at 0x10be30ed8>, ‘__doc__‘: None}

obj1 = Province(‘HeBei‘,10000)
print obj1.__dict__
# 获取 对象obj1 的成员
# 输出:{‘count‘: 10000, ‘name‘: ‘HeBei‘}

obj2 = Province(‘HeNan‘, 3888)
print obj2.__dict__
# 获取 对象obj1 的成员
# 输出:{‘count‘: 3888, ‘name‘: ‘HeNan‘}

  • __str__  如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值

class Foo:

    def __str__(self):
        return ‘wupeiqi‘

obj = Foo()
print obj
# 输出:wupeiqi

  • __getitem__、__setitem__、__delitem__  用于索引操作,如字典。分别表示获取、设置、删除数据

#!/usr/bin/env python
# -*- coding:utf-8 -*-

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‘] = ‘wupeiqi‘   # 自动触发执行 __setitem__
del obj[‘k1‘]           # 自动触发执行 __delitem__

  • __getslice__、__setslice__、__delslice__   该三个方法用于分片操作,如:列表

#!/usr/bin/env python
# -*- coding:utf-8 -*-

class Foo(object):

    def __getslice__(self, i, j):
        print ‘__getslice__‘,i,j

    def __setslice__(self, i, j, sequence):
        print ‘__setslice__‘,i,j

    def __delslice__(self, i, j):
        print ‘__delslice__‘,i,j

obj = Foo()

obj[-1:1]                   # 自动触发执行 __getslice__
obj[0:1] = [11,22,33,44]    # 自动触发执行 __setslice__
del obj[0:2]                # 自动触发执行 __delslice__

  • __iter__  用于迭代器,之所以列表、字典、元组可以进行for循环,是因为类型内部定义了 __iter__

class Foo(object):
    pass

obj = Foo()

for i in obj:
    print i

# 报错:TypeError: ‘Foo‘ object is not iterable

class Foo(object):

    def __iter__(self):
        pass

obj = Foo()

for i in obj:
    print i

# 报错:TypeError: iter() returned non-iterator of type ‘NoneType‘

class Foo(object):

    def __init__(self, sq):
        self.sq = sq

    def __iter__(self):
        return iter(self.sq)

obj = Foo([11,22,33,44])

for i in obj:
    print i

  • __new__和__metaclass__
class Foo(object):

    def __init__(self):
        pass

obj = Foo()   # obj是通过Foo类实例化的对象

上述代码中,obj 是通过 Foo 类实例化的对象,其实,不仅 obj 是一个对象,Foo类本身也是一个对象,因为在Python中一切事物都是对象

如果按照一切事物都是对象的理论:obj对象是通过执行Foo类的构造方法创建,那么Foo类对象应该也是通过执行某个类的 构造方法 创建。

print type(obj) # 输出:<class ‘__main__.Foo‘>     表示,obj 对象由Foo类创建
print type(Foo) # 输出:<type ‘type‘>              表示,Foo类对象由 type 类创建

所以,obj对象是Foo类的一个实例Foo类对象是 type 类的一个实例,即:Foo类对象 是通过type类的构造方法创建。

那么,创建类就可以有两种方式:

a). 普通方式

class Foo(object):

    def func(self):
        print ‘hello wupeiqi‘

b).特殊方式(type类的构造函数)

def func(self):
    print ‘hello wupeiqi‘

Foo = type(‘Foo‘,(object,), {‘func‘: func})
#type第一个参数:类名
#type第二个参数:当前类的基类
#type第三个参数:类的成员

==》 类 是由 type 类实例化产生

那么问题来了,类默认是由 type 类实例化产生,type类中如何实现的创建类?类又是如何创建对象?

答:类中有一个属性 __metaclass__,其用来表示该类由 谁 来实例化创建,所以,我们可以为 __metaclass__ 设置一个type类的派生类,从而查看 类 创建的过程。

class MyType(type):

    def __init__(self, what, bases=None, dict=None):
        super(MyType, self).__init__(what, bases, dict)

    def __call__(self, *args, **kwargs):
        obj = self.__new__(self, *args, **kwargs)

        self.__init__(obj)

class Foo(object):

    __metaclass__ = MyType

    def __init__(self, name):
        self.name = name

    def __new__(cls, *args, **kwargs):
        return object.__new__(cls, *args, **kwargs)

# 第一阶段:解释器从上到下执行代码创建Foo类
# 第二阶段:通过Foo类创建obj对象
obj = Foo()

                                                欢迎大家对我的博客内容提出质疑和提问!谢谢

                                                                             笔者:拍省先生  

 

时间: 2024-12-15 21:50:35

python基础-第七篇-7.2面向对象(进阶篇)的相关文章

Python基础-第七天-面向对象编程进阶和Socket编程简介

本篇内容: 1.面向对象编程进阶-静态方法 2.面向对象编程进阶-类方法 3.面向对象编程进阶-属性方法 4.面向对象编程进阶-特殊成员(内置方法) 5.面向对象编程进阶-反射 6.异常处理.断言 7.Socket编程简介 一.面向对象编程进阶-静态方法 1.静态方法的实现 通过@staticmethod装饰器可以把其装饰的方法变为一个静态方法: 变成静态方法后,形参中可以不用写self了.如果写了self,默认是不会把对象本身传递给self,需要手动传递: class Dog(object):

python基础-第七篇-7.1初识类和对象

创建类和对象 刚开始我们接触得多的编程方式为面向过程编程,这种方式就是根据业务逻辑从上往下垒代码,后来又出现了函数式编程,就是为了提高代码的重用性,减轻程序猿的工作量--而今天我们即将学的 面向对象编程则是使用类和对象来实现的,类就是一个模板,模板里可以包含多个函数,函数里实现一些功能 对象则是根据模板创建的实例,通过实例对象可以执行类中的函数 class是关键字,表示类 创建对象--类名称后加括号 #创建类 class foo: def bar(self): print('bar') def

python基础-第七篇-7.4异常处理

异常基础: 异常处理首先要捕获异常,不让程序中断,也不让错误信息直接呈现出来,然后就是你该怎么处理异常,以什么方式显示 try: pass except Exception,ex: pass 在需要用户输入数字的时候 while True: try: num1 = int(input('请输入第一个数字:')) num2 = int(input('请输入第二个数字:')) result = num1 + num2 print(result) except Exception as e: prin

python 基础第七篇

一. 补充基础数据类型的相关知识点 str. join() 把列表变成字符串 列表不能再循环的时候删除. 因为索引会跟着改变 字典也不能直接循环删除.把要删除的内容记录在列表中. 循环列表. 删除原列表, 字典中的数据 fromkeys() 不会对原来的字典产生影响. 产生新字典(神坑, 考试) set集合. 不重复, 无序. 想转换成什么.就用什么括起来 深浅拷贝 直接赋值. 两个变量指向同一个对象. 浅拷贝:只拷贝第一层内容. copy() 深度拷贝: 对象中的所有内容都会被拷贝一份 imp

python基础-第七篇-7.3反射

定义 反射是根据字符串的形式去对操作其成员 了解反射前,我先看看内置方法__import__,还记得这个内置方法吗? __import__  用于以字符串的形式导入模块 inp = input('请输入你要导入的模块:') mokuai = __import__(inp) #import xml as mokuai print(mokuai.__doc__) getattr  用于以字符串的形式去某个模块寻找函数 import re str_func = input('请输入你要执行re下的方法

python 面向对象 进阶篇

在上篇<python面向对象>中,简单介绍了python中面向对象的基本知识 在这篇博客中,详细介绍python类的成员,成员修饰符,类的特殊成员. 类的成员 类的成员分为三种:字段,方法和属性 所有成员中,只有普通字段的内容保存对象中,即:根据此类创建了多少对象,在内存中就有多少个普通字段.而其他的成员,则都是保存在类中,即:无论对象的多少,在内存中只创建一份. 字段 字段包括普通字段和静态字段.静态字段,保存在类中.普通字段,保存在对象中. class FOO: country = “中国

Python基础(七)内置函数

今天来介绍一下Python解释器包含的一系列的内置函数,下面表格按字母顺序列出了内置函数: 下面就一一介绍一下内置函数的用法: 1.abs() 返回一个数值的绝对值,可以是整数或浮点数等. 1 2 3 4 5 6 print(abs(-18))          print(abs(0.15)) result: 18 0.15 2.all(iterable) 如果iterable的所有元素不为0.''.False或者iterable为空,all(iterable)返回True,否则返回False

python之路 面向对象进阶篇

一.字段 字段包括:普通字段和静态字段,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同, 普通字段属于对象 静态字段属于类 class Province: # 静态字段 country = '中国' def __init__(self, name): # 普通字段 self.name = name # 直接访问普通字段 obj = Province('河北省') print obj.name # 直接访问静态字段 Province.country 由上述代码可以看出[普通字段需

Python 基础 - Day 5 Learning Note - 模块 之 介绍篇

定义 模块(module)支持从逻辑上组织Python代码,本质就是.py结尾的python文件(e.g.文件名:test.py; 模块名:test),目的是实现某项功能.将其他模块属性附加到你的模块中的操作叫导入(import). 模块分为三类:标准库.开源模块(open source module)和自定义模块. 包(package)是一个有层次的文件目录结构, 定义了一个由模块和子包组成的python应用程序执行环境.和模块及类一样,也使用句点属性标识来访问他们的元素.使用标准的impor