首页 > 技术文章 > python 面向对象练习

saoqiang 2020-03-10 09:04 原文

面向对象的结构与组成成员

类有两种属性:数据属性(静态等)和函数属性(方法 等)

成员有以下:
1、字段: 静态字段 普通字段
2、方法: 静态方法 类方法 普通方法
3、特性/属性 普通特性

成员修饰符 修饰成员
公有的:没有限制
私有的:以__开头 仅仅内部可以访问,不能被继承,仅自己可访问。私有的成员可通过公有的成员间接访问

何时用类调用,何时用对象调用?
类调用: 无self
对象调用:self

结论

1、静态字段和静态方法和类方法通过类来访问,普通字段和方法通过对象来访问
2、静态方法,使用@staticmethod来装饰
3、类方法,使用@classmethod来装饰
4、属性(特性),使用@property来装饰,定义的时候类似普通方法一样,但调用的时候像普通字段一样
5、属性的getter和setter,通过@property来装饰的方法return来getter,之后通过@方法名.setter装饰的方法来setter

一些特殊的成员

init 构造方法,创建对象时调用
del 析构方法,销毁对象时调用
call 对象() 调用
getitem 对象[] 来调用
setitem 对象[] = xxx 来调用
delitem del 对象[] 来调用
dict 列出所有的成员 用途:在表单对象中获取表单所有的字段
参考字典对象dict
str 类似java中的toString方法,直接打印对象输出的就是该方法的返回值

class A:
    company_name = 'mcsq'  # 静态变量(静态字段)
    __iphone = '1353333xxxx'  # 私有静态变量(私有静态字段)
    def __init__(self,name,age): #特殊方法
        self.name = name  #对象属性(普通字段)
        self.__age = age  # 私有对象属性(私有普通字段)
    def func1(self):  # 普通方法
        pass
    def __func(self): #私有方法
        print(666)
    @classmethod  # 类方法
    def class_func(cls):
        """ 定义类方法,至少有一个cls参数 """
        print('类方法')
    @staticmethod  #静态方法
    def static_func():
        """ 定义静态方法 ,无默认参数"""
        print('静态方法')
    @property  # 属性
    def prop(self):
        pass

obj = A()
obj.a_func(777)  # 对象也可以调用类方法,但是会自动将其从属于的类名传给cls

类的私有成员

# 类中的私有成员: 私有类的静态属性, 私有对象属性,私有方法
# 对于类的公有静态属性,类的外部,类的内部,派生类都可以访问.
# 私有静态属性: 类外部不能访问,类内部可以访问 ,派生类不可访问
# 对象的私有属性# 类的内部可以使用 ,类的外部不能访问 ,派生类中也不可访问

# 如果想设定一些私有的或者是不想让类外面用到,密码,加密方式,等设置成私有成员.
# 拓展: 私有成员除了在类内部,当真访问不到么?
# python中所有的私有成员: 就是在私有成员前面加上 _类名而已.
# print(A._A__girlnum)  # 千万不要这么去访问!!!

类方法与静态方法

# 类方法
# @classmethod  # 类方法: 由类名直接调用的方法,他会自动的将类名传给cls
# 对象也可以调用类方法,但是会自动将其从属于的类名传给cls
#  @staticmethod  静态方法: 不依赖于类,也不依赖于对象,他就是一个普通的函数放置于类中是结构更加清晰与合理.

属性

# 我们要让类方法伪装成属性,虽然在代码级别没有提升,但是看起来更合理.
# @property
# def aaa(self):
#     print('get的时候运行我啊')
# @aaa.setter
# def aaa(self,v):
#     print('修改的时候执行我')
# @aaa.deleter
# def aaa(self):
#     print('删除的时候执行我')

isinstance issubclass

# isinstance(obj,N): 判断 obj对象 是由N类(N的派生类)实例化的对象 返回 True.
# issubclass(M,N) 判断是M类是N类的子孙.

# type 到底是什么?
# type 元类 python中一切皆对象 , 一个类也是一个对象.
# 么这个(类)对象肯定是由类实例化出来的.
# python中你创建的所有类,以及大部分list str等等这些类,都是从type元类实例化得来的.
# python中继承object类都是新式类.
# object 也是由type元类实例化得来的.
# type

函数vs 方法

# 1 通过函数名可以大致判断
# print(func)  # <function func at 0x00000000005D1EA0> 函数
# obj = A()
# print(obj.func)  # <bound method A.func of <__main__.A object at 0x0000000001DE1CF8>> 方法

# 2. 通过types模块去验证
# from types import FunctionType #返回bool True就是函数
from types import MethodType
# 类名调用func 就是一个函数
# print(isinstance(A.func, FunctionType)) #True   函数
# 对象调用func 就是一个方法
# print(isinstance(obj.func, FunctionType))
# 对于静态方法的研究
# print(isinstance(A.f, FunctionType))

# 结论
# 1. 类⽅法.不论任何情况,都是⽅法.
# 2. 静态方法,不论任何情况.都是函数
# 3. 实例方法,如果是实例访问.就是⽅法.如果是类名访问就是函数.
# 函数与方法
# 函数: 全都是显性传参
# 方法: 存在隐性传参

面向对象的方法与调用

1.类名调用类中的属性和方法
注意 类名调用了方法必须穿参 不然报错 类名可以对类中的属性 方法增删改查
2.对象调用对象的属性和方法
对象可以操作对象空间的属性 万能的点 进行增删改查

面向对象中的self是什么

什么是self?
1.self 就是类中方法的第一个位置参数, 约定俗称: 类中的方法第一个参数一般都设置成self
2.如果通过对象执行此方法,解释器就自动的将此对象空间当做实参传给
3.谁调用他 他就是谁

先有对象还是先有类?

  • 在现实生活中:肯定是先有对象再有类,对象是具体的某个事物,真实存在的,而类是抽象的。仅仅是一个概念。比如,我们可以叫一个人的姓名就知道这个人是谁,而无法叫一个人类来代表某个人。
  • 在程序世界中:相反,我们必须先定义类,再实例化对象。比如:女娲造人这个神话故事,有一个泥娃娃模型(类)之后,可以造无数个人(对象)

面向对象组合和依赖

1. 依赖关系(主从关系)
    依赖关系: 将一个类的类名或者对象传给另一个类的方法中.
2. 组合关系(关联组合聚合)
    组合: 将一个类的对象封装成另一个类的对象的属性.
3. 继承关系
    单继承 多继承
    
好处 使类与类产出了关系

面向对象的三大特性 5大原则

面向对象的三大特性是"封装、"多态"、"继承",五大原则是"单一职责原则"、"开放封闭原则"、"里氏替换原则"、"依赖倒置原则"、"接口分离原则"。

封装:
    把很多数据封装到⼀个对象中. 把固定功能的代码封装到⼀个代码块, 函数, 对象, 打包成模块.这都属于封装的思想. 具体的情况具体分析. 比如. 你写了⼀个很⽜B的函数. 那这个也可以被称为封装. 在⾯向对象思想中. 是把⼀些看似⽆关紧要的内容组合到⼀起统⼀进⾏存储和使⽤. 这就是封装.
继承:
	⼦类可以⾃动拥有⽗类中除了私有属性外的其他所有内容
多态:
    python中 定义变量不用规定变量的类型.
    
鸭子类型(面试会问到)
    A,B两个类,没有任何关系,独立两个,但是里面的功能相似,所以python一般会将类似于A,B两个类里面的相似的功能让其命名相同.
    A,B虽然无关系,但是很默契的制定了一个规范.让你使用起来更方便.

https://www.cnblogs.com/corvoh/p/5747856.html

__init__ 与 __new__

1.__init__ 实列方法  通常用于初始化一个新实例,控制这个初始化的过程,比如添加一些属性, 做一些额外的操作,发生在类实例被创建完以后。它是实例级别的方法。
2.__new__ 构造方法通常用于控制生成一个新实例的过程。它是类级别的方法。

面向对象继承

什么是继承?
    专业角度: B 继承 A类, B就叫做A的子类,派生类,
    A叫做B的父类,基类,超类.
    B类以及B类的对象使用A类的所有的属性以及方法.
    字面意思: 继承就是继承父母所有的资产.  指向而已  自己空间并没有
继承的优点
    节省代码.
    增强的耦合性.
    代码规范化.
单继承
    1.子类对象执行父类的一切.
    2.实例化对象一定一定会执行三件事. 一定会执行__init__
    3.注意: 子类以及子类对象只能调用父类的属性以及方法,不能操作(增删改).
    4.子类将父类的方法覆盖了,(重写父类的方法) 调用就不找父类 类指针
多继承
     ⼀个叫经典类. 在python2.2之前. ⼀直使⽤的是经典类. 经典类在基类的根如果什么都不写.
      ⼀个叫新式类. 在python2.2之后出现了新式类. 新式类的特点是基类的根是object类。
        在经典类中采⽤的是深度优先,遍历方案. 什么是深度优先. 就是⼀条路走到头. 然后再回来. 继续找下⼀个.
        新式类的多继承一般我们用 mro方法就行

在其他语言中 可以实现多个接口 / 不可以继承多个类

# Java
interface IBase:
	def f1(sef)
	
interface INewBase:
	def f2(sef)
	
	
class Foo(IBase,INewBase):
	def f1(self):
    	pass
    def f2(self):
    	pass 


抽象类和抽象方法

  • 与Java一样。python也有抽象类的概念,但是需要借助模块实现
  • 抽象类是一个特殊的类,只能被继承,不能被实例化
  • 抽象类就是从多个类中抽取相同属性和方法。
  • 用来规范子类。子类必须要有抽象类当中的方法
#java
abstrac class Base(object): # 抽象类
	
	def f1(self): # 普通方法
		print(123)
		
	def abstract f2(self): # 抽象方法,内部不能写任何代码
		pass 
	
class Foo(Base):
	
    def f2(sef):
    	print(123)
    	
obj = Foo()
obj.f2()
obj.f1()

python中
# 抽象类 规范子类 只能有抽象方法  只能被继承,不能实例化
class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod  # 定义抽象方法,无需实现功能
    def run(self):
        """
        子类必须定义run()方法
        """
        pass

    @abc.abstractmethod  # 定义抽象方法,无需实现功能
    def eat(self):
        """
        子类必须定义eat()方法
        """
        pass


class Person(Animal):
    def run(self):
        print('在走')

    def eat(self):
        print('人吃饭')


class Pig(Animal):
    def run(self):
        print('在跑')

    def eat(self):
        print('猪吃草')


class Dog(Animal):
    def run(self):
        print('在跳')

    def eat(self):
        print('小狗吃肉')


class Cat(Animal):
    def run(self):
        pass

    def eat(self):
        pass


person1 = Person()
pig1 = Pig()
dog1 = Dog()

person1.run()
pig1.run()
dog1.run()

多态和鸭子类型

import abc


class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def run(self):
        pass

    @abc.abstractmethod
    def eat(self):
        pass


class Person(Animal):
    def run(self):
        print('在走')

    def eat(self):
        print('人吃饭')


class Pig(Animal):
    def run(self):
        print('在跑')

    def eat(self):
        print('猪吃草')


class Dog(Animal):
    def run(self):
        print('在跳')

    def eat(self):
        print('小狗吃肉')


class Cat(Animal):
    def run(self):
        pass

    def eat(self):
        pass
class Phone:
    def eat(self):
        print('我是手机对象,吃不得')

person1 = Person()
pig1 = Pig()
dog1 = Dog()
p = Phone()

# 统一的接口
def func(animal):
    animal.eat()


func(person1)
func(pig1)
func(dog1)
func(p)
'''
人吃饭
猪吃草
小狗吃肉
我是手机对象,吃不得
'''

对于animal来说。它是func的一个参数,而却代表着多种形态,动物。人。猫。狗。手机。
为什么要使用多态性?
增加量程序的灵活性。以不变应万变,不论对象千变万变,使用者都是通过接口func(animal)去调用
增加量程序的可扩展性。新建的类Phone。只要他下面有eat方法,使用者可以完全不修改自己的代码,和动物类一样调用eat()方法。


鸭子类型
如果看起来像鸭子,走起来像鸭子,叫起来像鸭子,那它就是鸭子。
序列类型有多种形态:字符串,列表,元组,但他们直接没有直接的继承关系
# 序列类型:列表list  元祖tuple  字符串str都可以用len()

类的约束

# 第二种: 固定写法
from abc import ABCMeta,abstractmethod
class Payment(metaclass=ABCMeta):
            # 抽象类 接口类  规范和约束  metaclass指定的是一个元类
    @abstractmethod
    def pay(self, money):
        pass  # 抽象方法
class Wechatpay(Payment):
    def fuqian(self,money):
        print(f'利用微信支付了{money}')
    # def pay(self,money):
    #     pass
obj3 = Wechatpay()
# 利用抽象类的概念: 基类如上设置,子类如果没有定义pay方法,在实例化对象时就会报错.

请用类代码简答实现stack 栈

  • Stack() 创建一个新的空栈
  • push(item) 添加一个新的元素item到栈顶
  • pop() 弹出栈顶元素
  • peek() 返回栈顶元素
  • is_empty() 判断栈是否为空
  • size() 返回栈的元素个数
# 实现一个栈stack,后进先出

'''
class Stack:
    def __init__(self):
        self.items = []

    def is_empty(self):
        # 判断是否为空
        return self.items == []

    def push(self,item):
        # 加入元素
        self.items.append(item)

    def pop(self):
        # 弹出元素
        return self.items.pop()

    def peek(self):
        # 返回栈顶元素
        return self.items[len(self.items)-1]

    def size(self):
        # 返回栈的大小
        return len(self.items)

if __name__ == "__main__":
    stack = Stack()
    stack.push("H")
    stack.push("E")
    stack.push("L")
    print(stack.size())  # 3
    print(stack.peek())  # L 
    print(stack.pop())   # L
    print(stack.pop())   # E
    print(stack.pop())   # H
'''

谈谈你对面向对象的理解

它可以有不同层次的理解:
从世界观的角度可以认为:面向对象的基本哲学是认为世界是由各种各样具有自己的运动规律和内部状态的对象所组成的;不同对象之间的相互作用和通讯构成了完整的现实世界。因此,人们应当按照现实世界这个本来面貌来理解世界,直接通过对象及其相互关系来反映世界。这样建立起来的系统才能符合现实世界的本来面目。
从方法学的角度可以认为:面向对象的方法是面向对象的世界观在开发方法中的直接运用。它强调系统的结构应该直接与现实世界的结构相对应,应该围绕现实世界中的对象来构造系统,而不是围绕功能来构造系统。

节省代码 结构更清晰

Python面向对象中的继承有什么特点

python 两种类:经典类 新式类
python3 新式类 —— 都默认继承object class Animal(object): == class Animal:
python2 经典类和新式类 并存
        class Animal:  经典类 —— 继承顺序 个别使用方法
        class Animal(object):  新式类

继承分为单继承和多继承
Python是支持多继承的
如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现。

1、对象可以调用自己本类和父类的所有方法和属性, 先调用自己的 自己没有才调父类的。谁(对象)调用方法,方法中的self就指向谁



class Foo:
    def __init__(self):
        self.func()

    def func(self):
        print('Foo.func')

class Son(Foo):
    def func(self):
        print('Son.func')

面向对象深度优先和广度优先是什么?

Python的类可以继承多个类,Python的类如果继承了多个类,那么其寻找方法的方式有两种
当类是经典类时,多继承情况下,会按照深度优先方式查找  py3
当类是新式类时,多继承情况下,会按照广度优先方式查找  py2
简单点说就是:经典类是纵向查找,新式类是横向查找
经典类和新式类的区别就是,在声明类的时候,新式类需要加上object关键字。在python3中默认全是新式类

面向对象中super的作用?

用于子类继承基类的方法

class FooParent(object):
    def __init__(self):
        self.parent = 'I\'m the parent.'
        print('Parent')
        print('1111')

    def bar(self, message):
        print("%s from Parent" % message)


class FooChild(FooParent):
    def __init__(self):
        # super(FooChild,self) 首先找到 FooChild 的父类(就是类 FooParent),然后把类B的对象 FooChild 转换为类 FooParent 的对象
        super(FooChild, self).__init__()
        print('Child')

    # def bar(self, message):
    #     # super(FooChild, self).bar(message)
    #     print('Child bar fuction')
    #     print(self.parent)


if __name__ == '__main__':
    fooChild = FooChild()
    fooChild.bar('HelloWorld')

列举面向对象中带双下划线的特殊方法,如:newinit

__new__:生成实例
__init__:生成实例的属性
__call__:实例对象加( )会执行def __call__:... 方法里边的内容。

__del__:析构方法,当对象在内存中被释放时,自动触发执行。如当 del obj 或者应用程序运行完毕时,执行该方法里边的内容。

__enter__和__exit__:出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量;with中代码块执行完毕时执行__exit__里边的内容。

__module__:表示当前操作的对象在那个模块   obj.__module__
__class__ :表示当前操作的对象的类是什么     obj.__class__

__doc__:类的描述信息,该描述信息无法被继承

__str__:改变对象的字符串显示 print函数 --->obj.__str__()
__repr__:改变对象的字符串显示 交互式解释器 --->obj.__repr__()
__format__:自定制格式化字符串

__slots__:一个类变量 用来限制实例可以添加的属性的数量和类型  

__setitem__,__getitem,__delitem__:

如何判断是函数还是方法?

看他的调用者是谁,如果是类,就需要传入一个参数self的值,这时他就是一个函数,

如果调用者是对象,就不需要给self传入参数值,这时他就是一个方法

print(isinstance(obj.func, FunctionType))   # False

print(isinstance(obj.func, MethodType))    # True

函数:

  函数是封装了一些独立的功能,可以直接调用,python内置了许多函数,同时可以自建函数来使用。

方法:

  方法和函数类似,同样封装了独立的功能,但是方法是需要通过对象来调用的,表示针对这个对象要做的操作,使用时采用点方法。

静态方法和类方法区别?

尽管 classmethod 和 staticmethod 非常相似,但在用法上依然有一些明显的区别。classmethod 必须有一个指向类对象的引用作为第一个参数,而 staticmethod 可以没有任何参数。

class Num:
    # 普通方法:能用Num调用而不能用实例化对象调用   
    def one():  
        print ('1')
 
    # 实例方法:能用实例化对象调用而不能用Num调用
    def two(self):
        print ('2')
 
    # 静态方法:能用Num和实例化对象调用
    @staticmethod 
    def three():  
        print ('3')
 
    # 类方法:第一个参数cls长什么样不重要,都是指Num类本身,调用时将Num类作为对象隐式地传入方法   
    @classmethod 
    def go(cls): 
        cls.three() 
 
Num.one()          #1
#Num.two()         #TypeError: two() missing 1 required positional argument: 'self'
Num.three()        #3
Num.go()           #3
 
i=Num()                
#i.one()           #TypeError: one() takes 0 positional arguments but 1 was given         
i.two()            #2      
i.three()          #3
i.go()             #3 

列举面向对象中的特殊成员以及应用场景

__call__

__new__

__init__

__doc__

__class__

__del__

__dict__

__str__

在falsk源码用到......

什么是反射?以及应用场景?

反射的核心本质就是以字符串的形式去导入个模块,利用字符串的形式去执行函数。

Django中的 CBV就是基于反射实现的。

4个方法
getattr、hasattr、delattr和setattr较为全面的实现了基于字符串的反射机制
# 总结
# print(hasattr(obj,'name'))#查询 返bool 确认 有没有这个属性
# print(getattr(obj,'name','没有'))#获取 返回 对应属性值  加第3个参数 不会报错
# setattr(obj,'aa','aa')#设置属性第2个参数是键第3个是值   可以覆盖
# delattr(obj,'name')#删除属性

metaclass作用?以及应用场景?

不知道
class MyType(type):
    def __call__(self, *args, **kwargs):
        return 'MyType'
  
  
class Foo(object, metaclass=MyType):
    def __init__(self):
        return 'init'
  
    def __new__(cls, *args, **kwargs):
        return cls.__init__(cls)
  
    def __call__(self, *args, **kwargs):
        return 'call'
  
  
obj = Foo()
print(obj)  # MyType

用尽量多的方法实现单例模式。

1:使用模块
Python的模块就是天然的单例模式。
因为模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码。
因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。
例如:
class V1(object):
    def foo(self)
        pass
V1 = V1()
将上面代码保存在文件test.py,要使用时,直接在其他文件中导入此文件中的对象,这个对象既是单例模式的对象

如:from a import V1

2:使用装饰器
def Singleton(cls):
    _instance = {}
    def _singleton(*args, **kargs):
        if cls not in _instance:
            _instance[cls] = cls(*args, **kargs)
        return _instance[cls]
    return _singleton
@Singleton
class A(object):
    a = 1
    def __init__(self, x=0):
        self.x = x
a1 = A(2)
a2 = A(3)


3:使用类

4:基于__new__方法实现
当我们实例化一个对象时,是先执行了类的__new__方法
当:(我们没写时,默认调用object.__new__),实例化对象;然后再执行类的__init__方法,对这个对象进行初始化,所有我们可以基于这个,实现单例模式

什么是面向对象的mro

mro就是方法解析顺序
super 继承都用到

isinstance作用以及应用场景?

isinstance(对象,类)  判断这个对象是不是这个类或者这个类的子类的实例化

# # 判断a 属不属于A这个类(可以判断到祖宗类)
class A:
    pass

class B(A):
    pass
a = A()
b = B()
print(isinstance(b,A)) # ===> True  判断到祖宗类

# 任何与object都是True,内部都继承object
class A:pass
a = A()  # 实例化
print(isinstance(a,object))  #  True

什么是断言?应用场景?

Python assert(断言)用于判断一个表达式,在表达式条件为 false 的时候触发异常。

断言可以在条件不满足程序运行的情况下直接返回错误,而不必等待程序运行后出现崩溃的情况,


# 异常处理总结:
    # 异常处理不能经常使用:异常处理耗费性能.有些错误是需要进行分流使用.代码的可读性变差.
    # 关键节点使用.
# 万能异常 Exception as e: 可以起别名
# 什么时候用万能异常,什么时候用多分支?
#如果你只是想把这个异常处理掉,让程序继续执行. 万能异常.
# 如果出现了异常,你是想根据不同的异常执行不同的逻辑流程,你要采取多分支.

推荐阅读