Python面向对象

                             Python面向对象操作

1.类和对象

面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用
类 是一个模板,模板中包装了多个“函数”供使用(可以讲多函数中公用的变量封装到对象中)
对象,根据模板创建的实例(即:对象),实例用于调用被包装在类中的函数
面向对象三大特性:封装、继承和多态
python面向对象的重要术语

术语描述
类(Class):用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
方法:类中定义的函数。
类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
数据成员:类变量或者实例变量用于处理类及其实例对象的相关的数据。
方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
局部变量:定义在方法中的变量,只作用于当前实例的类。
实例变量:在类的声明中,属性是用变量来表示的,这种变量就称为实例变量,实例变量就是一个用 self 修饰的变量。
继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例图,Dog是一个Animal)。
实例化:创建一个类的实例,类的具体对象。
对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法

Python中的类提供了面向对象编程的所有基本功能:类的继承机制允许多个基类,派生类可以覆盖基类中的任何方法,方法中可以调用基类中的同名方法。

2.类定义

class ClassName: . . .
类实例化后,可以使用其属性,实际上,创建一个类之后,可以通过类名访问其属性。

3.类属性

属性引用(类名.属性)

class Person: #定义一个人类
    role = 'Person' #人的角色属性都是人
    def walk(self): #人都可以走路,也就是走路方法
        print('我是人,我可以走路')

print(Person.role) #查看人的角色属性
print(Person.walk) #引用人的走路方法,注意,这里不是在调用

输出结果:

Person
<function Person.walk at 0x000001480C02C268>

实例化:类名加括号就是实例化,会自动触发__init__函数的运行,可以用它来为每个实例定制自己的特征

class Person: #定义一个人类
    role = 'Person' #人的角色属性都是人
    def __init__(self,name): #构造函数
        self.name = name#每个人都有自己的名字
        print('我的名字是:',self.name)
    def walk(self): #人都可以走路,也就是走路方法
        print('我是人,我可以走路')

obj = Person('卢阿涛') #实例化
print(obj.role)
obj.walk()

输出结果:

我的名字是: 卢阿涛
Person
我是人,我可以走路

4.关于self

self:在实例化时自动将对象/实例本身传给__init__的第一个参数,你也可以给他起个别的名字,但是正常人都不会这么做。

self 代表的是类的实例,代表当前对象的地址,而 self.class 则指向类。

5.类的方法

在类的内部,使用 def 关键字来定义一个方法,与一般函数定义不同,类方法必须包含参数 self, 且为第一个参数,self 代表的是类的实例
面向对象的三种方法
静态方法(用这个装饰器来表示@staticmethod)
意思是把 @staticmethod 下面的函数和所属的类截断了,这个函数就不属于这个类了,没有类的属性了,只不是还是要通过类名的方式调用

class Person: #定义一个人类
    role = 'Person' #人的角色属性都是人
    def __init__(self,name): #构造函数
        self.name = name#每个人都有自己的名字
        print('我的名字是:',self.name)
    def walk(self): #人都可以走路,也就是走路方法
        print('我是人,我可以走路')
    @staticmethod  #把eat方法变为静态方法
    def eat(self): #吃
        print('我叫%s,我要吃东西了'%(self.name))
        
obj = Person('卢阿涛') #实例化
obj.walk()
obj.eat()#这个方法是静态的,不能够被调用

输出结果

我的名字是: 卢阿涛
我是人,我可以走路
Traceback (most recent call last):
File “C:\Users\冷月枫\Desktop\my.py”, line 14, in
obj.eat()#这个方法是静态的,不能够被调用
TypeError: eat() missing 1 required positional argument: ‘self’

因为用静态方法把eat这个方法与Person这个类截断了,eat方法就没有了类的属性了,所以获取不到self.name这个变量。如果不访问类属性,使用静态方法实际上就是把和这个方法独立出来,作为一个正常的函数使用就可以。
类方法 (用这个装饰器来表示 @classmethod)
类方法只能访问类变量,不能访问实例变量
错误示范:

class Person: #定义一个人类
    role = 'Person' #人的角色属性都是人
    def __init__(self,name): #构造函数
        self.name = name#每个人都有自己的名字
        print('我的名字是:',self.name)
    def walk(self): #人都可以走路,也就是走路方法
        print('我是人,我可以走路')
    @classmethod  #把eat方法变为类方法
    def eat(self): #吃
        print('我叫%s,我要吃东西了'%(self.name))
        
obj = Person('卢阿涛') #实例化
obj.walk()
obj.eat()

输出结果:

我的名字是: 卢阿涛
我是人,我可以走路
Traceback (most recent call last):
File “C:\Users\冷月枫\Desktop\my.py”, line 14, in
obj.eat()
File “C:\Users\冷月枫\Desktop\my.py”, line 10, in eat
print(‘我叫%s,我要吃东西了’%(self.name))
AttributeError: type object ‘Person’ has no attribute ‘name’

因为self.name这个变量是实例化这个类传进去的,类方法是不能访问实例变量的,只能访问类里面定义的变量
正确示范:

class Person: #定义一个人类
    role = 'Person' #人的角色属性都是人
    name = 'luatao'
    def __init__(self,name): #构造函数
        self.name = name#每个人都有自己的名字
        print('我的名字是:',self.name)
    def walk(self): #人都可以走路,也就是走路方法
        print('我是人,我可以走路')
    @classmethod  #把eat方法变为类方法
    def eat(self): #吃
        print('我叫%s,我要吃东西了'%(self.name))
        
obj = Person('卢阿涛') #实例化
obj.walk()
obj.eat()

输出结果:

我的名字是: 卢阿涛
我是人,我可以走路
我叫luatao,我要吃东西了

属性方法 (用这个装饰器表示 @property)
把一个方法变成一个静态属性,属性就不用加小括号那样的去调用了
错误示范:

class Person: #定义一个人类
    role = 'Person' #人的角色属性都是人
       def __init__(self,name): #构造函数
        self.name = name#每个人都有自己的名字
        print('我的名字是:',self.name)
    def walk(self): #人都可以走路,也就是走路方法
        print('我是人,我可以走路')
    @property  #把eat方法变为属性方法
    def eat(self): #吃
        print('我叫%s,我要吃东西了'%(self.name))
        
obj = Person('卢阿涛') #实例化
obj.walk()
obj.eat()

输出结果:

我的名字是: 卢阿涛
我是人,我可以走路
我叫卢阿涛,我要吃东西了
Traceback (most recent call last):
File “C:\Users\冷月枫\Desktop\my.py”, line 15, in
obj.eat()
TypeError: ‘NoneType’ object is not callable

因为eat此时已经变成一个属性了, 不是方法了, 想调用已经不需要加()号了,直接obj.eat就可以了
正确示范:

class Person: #定义一个人类
    role = 'Person' #人的角色属性都是人
       def __init__(self,name): #构造函数
        self.name = name#每个人都有自己的名字
        print('我的名字是:',self.name)
    def walk(self): #人都可以走路,也就是走路方法
        print('我是人,我可以走路')
    @property  #把eat方法变为属性方法
    def eat(self): #吃
        print('我叫%s,我要吃东西了'%(self.name))
        
obj = Person('卢阿涛') #实例化
obj.walk()
obj.eat

输出结果:

我的名字是: 卢阿涛
我是人,我可以走路
我叫卢阿涛,我要吃东西了

6.继承

Python 同样支持类的继承,如果一种语言不支持继承,类就没有什么意义。
猫可以:喵喵叫、吃、喝、拉、撒
狗可以:汪汪叫、吃、喝、拉、撒
公共的部分就是 吃、喝、拉、撒

class Animal:#定义一个动物类 就是共同的方法
    def eat(self):
        print('%s 吃' %(self.name))
    def drink(self):
        print('%s 喝' %(self.name))
class Cat(Animal): #继承动物类
    def __init__(self,name):
        self.name = name
        self.bread = '猫'
    def cry(self):
        print('喵喵喵')
class Dog(Animal):
    def __init__(self,name):
        self.name = name
        self.bread = '狗'
    def cry(self):
        print('汪汪汪')

cat1 = Cat('猫1')
cat1.eat()

cat2 = Cat('猫2')
cat2.drink()
cat2.cry()

dog1 = Dog('狗1')
dog1.eat()
dog1.cry()

输出结果:

猫1 吃
猫2 喝
喵喵喵
狗1 吃
汪汪汪

注意:多继承

  • 在Python中,如果父类和子类都重新定义了构造方法init( ),在进行子类实例化的时候,子类的构造方法不会自动调用父类的构造方法,必须在子类中显示调用。
  • Python的类可以继承多个类,Java和C#中则只能继承一个类
  • Python的类如果继承了多个类,那么其寻找方法的方式有两种,分别是:深度优先和广度优先
  • 当类是经典类时,多继承情况下,会按照深度优先方式查找,当类是新式类时,多继承情况下,会按照广度优先方式查找

经典类和新式类,从字面上可以看出一个老一个新,新的必然包含了跟多的功能,也是之后推荐的写法,从写法上区分的话,如果 当前类或者父类继承了object类,那么该类便是新式类,否则便是经典类。

class Person:#定义一个人类 就是共同的方法
    def __init__(self):
        pass
    def eat():
        print('卢阿涛 吃' )
    def drink(self):
        print('%s 喝' %(self.name))
    
class Student(Person): #继承人类
    def __init__(self):
        pass
    def cry():
        print('喵喵喵')
class Sperker:
    def __init__(self):
        pass
    def cry():
        print('汪汪汪')
#多重继承
class Sample(Sperker,Student):
    def __init__(self):
        Sperker.cry()
        Student.cry()
        Person.eat()
        

sample = Sample()

输出结果:

汪汪汪
喵喵喵
卢阿涛 吃

7.方法重写

如果你的父类方法的功能不能满足你的需求,你可以在子类重写你父类的方法,

class Person:#定义一个人类 就是共同的方法
    def eat(self):
        print('调用父类方法')

class Student(Person):
    def eat(self): #重写了父类的方法,会覆盖父类的方法
        print('调用子类的方法')
        

c = Student() #子类实例
c.eat() #子类重写的方法
super(Student,c).eat() #用子类对象调用父类已被覆盖的方法

输出结果:

调用子类的方法
调用父类方法

8.多态(Polymorphism)

首先Python不支持多态,也不用支持多态,python是一种多态语言,崇尚鸭子类型

class A:
    def eat(self):
        print('A')
class B(A):
    def eat(self):
        print('B')
class C(A):
    def eat(self):
        print('C')

class D(A):
    pass

class E:
    def eat(self):
        print('E')

class F:
    pass

def test(arg):
    arg.eat()

a = A()
b = B()
c = C()
d = D()
e = E()
f = F()

test(a)
test(b)
test(c)
test(d)
test(e)
test(f)

输出结果:

A
B
C
A
E
Traceback (most recent call last):
File “C:\Users\冷月枫\Desktop\my.py”, line 36, in
test(f)
File “C:\Users\冷月枫\Desktop\my.py”, line 22, in test
arg.eat()
AttributeError: ‘F’ object has no attribute ‘eat’

没有谁规定test方法是接收的参数是什么类型的。test方法只规定,接收一个参数,调用这个参数的eat方法。在运行的时候如果这个参数有eat方法,python就执行,如果没有,python就报错,因为abcde都有eat方法,而f没有,所以得到了上边得结果,这就是python的运行方式。

9.成员修饰符

python的类中只有私有成员和公有成员两种,不像c++中的类有公有成员(public),私有成员(private)和保护成员(protected).并且python中没有关键字去修饰成员,默认python中所有的成员都是公有成员,但是私有成员是以两个下划线开头的名字标示私有成员,私有成员不允许直接访问,只能通过内部方法去访问,私有成员也不允许被继承

class a: #说明父类的私有成员无法在子类中继承
    def __init__(self):
        self.leg = 'nihao'
        self.__page = 18

class b(a): #继承a
    def __init__(self,name):
        self.name = name
        self.__age = 18
        super(b,self).__init__() #这一行会报错
    def show(self):
        print(self.name)
        print(self.__age)
        print(self.leg)
        print(self.__page) #这一行也会报错
bb = b('luatao')
print(bb.name)
print(bb.leg)
#print(bb.__page)#这个也会报错
bb.show()

输出结果:

luatao
nihao
luatao
18
nihao
Traceback (most recent call last):
File “C:\Users\冷月枫\Desktop\my.py”, line 20, in
bb.show()
File “C:\Users\冷月枫\Desktop\my.py”, line 15, in show
print(self.__page) #这一行也会报错
AttributeError: ‘b’ object has no attribute ‘_b__page’

10.特殊成员

init
_init__方法可以简单的理解为类的构造方法(实际并不是构造方法,只是在类生成对象之后就会被执行)
del
__del__方法是类中的析构方法,当对象消亡的时候(被解释器的垃圾回收的时候会执行这个方法)这个方法默认是不需要写的,不写的时候,默认是不做任何操作的。因为你不知道对象是在什么时候被垃圾回收掉,所以,除非你确实要在这里面做某些操作,不然不要自定义这个方法。
call
__call__方法在类的对象被执行的时候(obj()或者 类()())会执行。
int
int__方法,在对象被int()包裹的时候会被执行,例如int(obj)如果obj对象没有、__int__方法,那么就会报错。在这个方法中返回的值被传递到int类型中进行转换。
str
__str__方法和int方法一样,当对象被str(obj)包裹的时候,如果对象中没有这个方法将会报错,如果有这个方法,str()将接收这个方法返回的值在转换成字符串。
add
__add__方法在两个对象相加的时候,调用第一个对象的__add__方法,将第二个对象传递进来,至于怎么处理以及返回值,那是程序员自定义的

class abc:
    def __init__(self,age):
        self.age = age
    def __add__(self,obj):
        return self.age + obj.age

a1 = abc(18)
a2 = abc(20)
print(a1 + a2)

输出结果:

38

dict
__dict__方法在类里面有,在对象里面也有,这个方法是以字典的形式列出类或对象中的所有成员。

class abc:
    def __init__(self,age):
        self.age = age
    def __add__(self,obj):
        return self.age + obj.age

a1 = abc(18)
print(abc.__dict__,'\n')
print(a1.__dict__)

输出结果:

{‘module’: ‘main’, ‘init’: <function abc.init at 0x00000272B94AC268>, ‘add’: <function abc.add at 0x00000272BBA28510>, ‘dict’: <attribute ‘dict’ of ‘abc’ objects>, ‘weakref’: <attribute ‘weakref’ of ‘abc’ objects>, ‘doc’: None}

{‘age’: 18}

getitem setitem delitem

__getitem__方法匹配 对象[索引] 这种方式,__setitem__匹配 对象[索引]=value 这种方式,__delitem__匹配 del 对象[索引] 这种方式,

class abc:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def __getitem__(self,item):#匹配:对象[item]这种形式
        return item + 10
    def __setitem__(self,key,value): #匹配:对象[key] =value这种形式
        print(key,value)
    def __delitem__(self,key): #匹配:del 对象[key]这种形式
        print(key)

a = abc('luatao',18)
print(a[10])
a[10] = 100
del a[10]

输出结果:

20
10 100
10

iter

类的对象如果想要变成一个可迭代对象,那么对象中必须要有__iter__方法,并且这个方法返回的是一个迭代器。

for 循环的对象如果是一个可迭代的对象,那么会先执行对象中的__iter__方法,获取到迭代器,然后再执行迭代器中的__next__方法获取数据。如果for循环的是一个迭代器,那么直接执行迭代器中的__next__方法

class abc:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def __iter__(self):
        return iter([1,2,3,4,5]) #返回的是一个迭代器

    
a = abc('luatao',18)
#1.如果类中有__iter__方法 他的对象就是可迭代对象
#2.对象.__iter()的返回值是一个迭代器
#3.for循环的如果是迭代器,直接执行.next()方法
#4.for循环的如果是可迭代对象,先执行对对象.__iter().获取迭代器再执行next

for i in a:
    print(i)

输出结果:

1
2
3
4
5

isinstance和issubclass

之前讲过isinstance可以判断一个变量是否是某一种数据类型,其实,isinstance不只可以判断数据类型,也可以判断对象是否是这个类的对象或者是这个类的子类的对象

issubclass用来判断一个类是否是某个类的子类,返回的是一个bool类型数据

class abc:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    
class bb(abc):
    pass

a = bb('luatao',18) 
print(isinstance(a,abc))  #判断一个对象是否是这个类的对象或这个类子类的对象
print(issubclass(son,abc)) #判断一个类是否是一个类的子类

输出结果:

True
True
类与对象

new__和__metaclass
在python中,一切皆对象,我们定义的类其实。。。也是一个对象,那么,类本身是谁的对象呢?在python2.2之前(或者叫经典类中),所有的类,都是class的对象,但是在新式类中,为了将类型(int,str,float等)和类统一,所以,所有的类都是type类型的对象。当然,这个规则可以被修改,在类中有一个属性 metaclass 可以指定当前类该由哪个类进行实例化。而创建对象过程中,其实构造器不是__init__方法,而是__new__方法,这个方法会返回一个对象,这才是对象的构造器。下面是一个解释类实例化对象内部实现过程的代码段:

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)
        self.__init__(obj,*args,**kwargs)
        return obj
class a:
    __metaclass = Mytype
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def __new__(cls,*args,**kwargs):
        return object.__new__(cls)

obj = a('luatao',18)
print(obj.name,obj.age)

输出结果:

luatao 18

11.反射/自省

python中的反射/自省的实现,是通过hasattr、getattr、setattr、delattr四个内置函数实现的,其实这四个内置函数不只可以用在类和对象中,也可以用在模块等其他地方,只是在类和对象中用的很多,所以单独提出来进行解释。

hasattr(key)返回的是一个bool值,判断某个成员或者属性在不在类或者对象中
getattr(key,default=xxx)获取类或者对象的成员或属性,如果不存在,则会抛出AttributeError异常,如果定义了default那么当没有属性的时候会返回默认值。
setattr(key,value)假如有这个属性,那么更新这个属性,如果没有就添加这个属性并赋值value
delattr(key)删除某个属性

注意,上面的key都是字符串,而不是变量,也就是说可以通过字符串处理类中的成员或者对象中的属性

class a:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def show(self):
        return self.name,self.age
aa = a('luatao',18)
print(getattr(aa,'name'))

setattr(aa,'k1','v1')
print(aa.k1)

print(hasattr(aa,'k1'))

delattr(aa,'k1')

show_fun = getattr(aa,"show")
print(show_fun())

输出结果:

luatao
v1
True
(‘luatao’, 18)

反射/自省能够直接访问以及修改运行中的类和对象的成员和属性,这是一个很强大的功能,并且并不像java中效率很低,所以用的很多。

12.单例模式

这里介绍一个设计模式,设计模式在程序员写了两三年代码的时候,到一定境界了,才会考虑到设计模式对于程序带来的好处,从而使用各种设计模式,这里只是简单的介绍一个简单的设计模式:单例模式。在面向对象中的单例模式就是一个类只有一个对象,所有的操作都通过这个对象来完成,这就是面向对象中的单例模式

class Foo:  # 单例模式
    __v=None
    @classmethod
    def ge_instance(cls):
        if cls.__v:
            return cls.__v
        else:
            cls.__v=Foo()
            return cls.__v
obj1=Foo.ge_instance()
print(obj1)
obj2=Foo.ge_instance()
print(obj2)
obj3=Foo.ge_instance()
print(obj3)

输出结果:

<main.Foo object at 0x0000023987CFD470>
<main.Foo object at 0x0000023987CFD470>
<main.Foo object at 0x0000023987CFD470>

可以看到,三个对象的内存地址都是一样的,其实,这三个变量中存储的都是同一个对象的内存地址,这样有什么好处呢?能够节省资源,就比如在数据库连接池的时候就可以使用单例模式,只创建一个类的对象供其他程序调用,还有在web服务中接收请求也可以使用单例模式来实现,节省资源。

13.面向对象的组合用法

组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合

class Weapon:
    life_value = 1000#初始生命值1000
    def prick(self):#装备的主动技能
        self.life_value -= 500 #攻击力是500
        print(self.life_value)
class Person:
    role = 'Person'

    def __init__(self,name):
        self.name = name
        self.weapon = Weapon() #给角色绑定一个武器

a = Person('luatao')
a.weapon.prick() #a对象组合了一个武器的对象,可以直接使用方法

输出结果:

500
————————————————

又是3个小时,好家伙,一上午又过去。
大部分都是参考的这篇文章,写的特别好,其他参考的比较少,就不贴链接了。
原文链接:https://blog.csdn.net/weixin_44239490/article/details/86357989

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值