【python】一次性学懂python的面向对象


1.类与对象的定义

1.1 全都是对象

  • python完全支持面向对象的基本功能:如封装、继承、多态以及对基类的重写和覆盖
  • python中一切内容都是对象,包括字符串、列表、元组、字典、集合、range对象等,函数也是对象、类也是对象

1.2 创建第一个完整的类

#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
class Employee:
   '所有员工的基类'
   empCount = 0
 
   def __init__(self, name, salary):
      self.name = name
      self.salary = salary
      Employee.empCount += 1
   
   def displayCount(self):
     print "Total Employee %d" % Employee.empCount
 
   def displayEmployee(self):
      print "Name : ", self.name,  ", Salary: ", self.salary
 
"创建 Employee 类的第一个对象"
emp1 = Employee("Zara", 2000)
"创建 Employee 类的第二个对象"
emp2 = Employee("Manni", 5000)
emp1.displayEmployee()
emp2.displayEmployee()
print "Total Employee %d" % Employee.empCount

1.3 构造方法

Python中的构造函数时__init__(),一般来说时给类的属性设置初值或者对其它必要的初始化工作,在创建对象的时候会被自动执行。在__init__()方法中定义的属性是实例属性,可以记录这个对象的特别信息。在类的方法中可以通过对self属性的方式获得__init__()方法中初始的值。

class people(object):

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

    def func(self):
        print(f"{self.name}的性别是{self.gender}")

面向对象编程的一个特点就是数据封装,在people类的__init__方法中初始化的实例属性name和gender,可以在类的方法通过类的对象访问类的实例属性。

if __name__ == '__main__':
    xiaoming = people("小明", "男")
    xiaoming.func()

输出结果:小明的性别是男

也可以通过类的对象访问实例属性,类的实例属性可以被类的对象访问和修改

if __name__ == '__main__':
    xiaoming = people("小明", "男")
    xiaoming.name = "张三"
    xiaoming.func()
结果:张三的性别是男

2.私有属性和方法

在类的内部中可以定义属性和方法,在方法内封装复杂的业务逻辑,可以通过类的对象访问方法。但是类的属性可以在类的内部进行访问和修改,也可以通过类的对象进行访问和修改,这样会破坏类的封装性。

在java中我们可以设置javabean,用private修改属性的访问权限,给属性添加get和set方法。那么在python中我们要私用属性和方法改怎么做呢?我们可以在属性的名称前面加上两个下划线"__"。

2.1 定义私有属性

python中定义类的属性时,如果属性的名称以两个下划线开头则是表示为私有属性。私有属性在类的外部不能访问,需要通过类调用对象的公有方法进行访问。子类可以继承父类的公有成员,但是不能继承私有成员。

class people(object):

    def __init__(self, name, gender):
        self.__name = name
        self.__gender = gender

    def func(self):
        print(f"{self.__name}的性别是{self.__gender}")


if __name__ == '__main__':
    xiaoming = people("小明", "男")
    xiaoming.name = "张三"
    xiaoming.func()
输出结果:小明的性别是男

我们发现不能再通过对象.属性方式修改类的成员变量,并且当我们打印print(xiaoming.__name) 发现程序报错:

AttributeError: ‘people’ object has no attribute ‘__name’

2.2 定义私有方法

除了定义私有属性,还可以定义私有方法,在方法名前面加上两个下划线即可。那么我们如何访问私有方法呢,很简单,通过一个公有方法去调用私有方法即可。

class people(object):

    def __init__(self, name, gender):
        self.__name = name
        self.__gender = gender

    def __func(self):
        print(f"{self.__name}的性别是{self.__gender}")

    def info(self):
        self.__func()


if __name__ == '__main__':
    xiaoming = people("小明", "男")
    xiaoming.info()
输出结果:小明的性别是男

3.继承

在OOP(面向对象程序设计)中继承是为了代码复用和设计复用而设计的。设计一个新类时,如果可以继承一个已有的设计好的类进行二次开发,可以大幅度减少代码量。在继承关系中,已有的,设计好的类被称为父类或者基类,新设计的类被成为子类或者派生类,享有父类的属性和方法。在python中同时支持单继承和多继承(在java中只支持单继承,多继承要通过接口来实现)

3.1 单继承

python中单继承语法:

class newclass(parentclass):
	pass

当子类继承父类时,在子类的构造函数中调用父类的构造函数有两种写法:

  • 经典类的写法:父类._init_(self,kwargs1,kwargs2,…)
  • 新式类的写法: super(子类,self)._init_(self,kwargs1,kwargs2,…)

本例编写两个类,一个people类,一个student,student继承people

class people(object):

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

    def func(self):
        print(f"{self.name}的性别是{self.gender}")



class student(people):
    def __init__(self,name,gender,age):
        super(student, self).__init__(name, gender)
        self.age = age

    def stu_fun(self):
        print(f"{self.name}的性别是{self.gender},年龄是{self.age}")

if __name__ == '__main__':
    stu = student("小明","男","18")
    stu.stu_fun()
    stu.func()
结果:
小明的性别是男,年龄是18
小明的性别是男

3.2 子类对父类方法的重写

有时候,子类不想继承父类的方法,而是对父类的方法进行一定的修改,这个时候就要对父类方法进行重写。如果子类和父类方法有相同的方法名,输入参数和返回类型,那么新方法会覆盖旧方法。
如果在子类中要调用父类的方法,可以用super()方法名的方式进行调用。

class people(object):

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

    def func(self):
        print(f"{self.name}的性别是{self.gender}")


class student(people):
    def __init__(self, name, gender, age):
        super(student, self).__init__(name, gender)
        self.age = age

    def func(self):
        super().func()
        print(f"{self.name}的性别是{self.gender},年龄是{self.age}")


if __name__ == '__main__':
    stu = student("小明", "男", "18")
    stu.func()

结果:
小明的性别是男
小明的性别是男,年龄是18

本例中,student类继承了people类,在student类中重写了父类的方法func(),还在子类重写的方法中调用了父类的方法。

3.3 多继承(继承下的多态)

什么叫做多态呢?
当子类和父类都有相同的方法时,子类的方法覆盖父类的方法,在代码运行的时候,总是会调用子类的方法,这就是多态。

python中多继承语法:

class newclass(parentclass1,parentclass2,...)
	pass

需要注意的是newclass后面的父类顺序,如果父类中有相同的方法名,而在子类中没有使用指定的父类名,则python解释器会从左向右按顺序进行搜索,如果方法在子类中没找到,则会从左向右查找父类中是否包含该方法。

class Man(object):

    def __init__(self, name):
        self.name = name
        self.gender = "男"

    def func(self):
        print(f"{self.name}的性别是{self.gender}")


class Woman(object):

    def __init__(self, name):
        self.name = name
        self.gender = "女"

    def func(self):
        print(f"{self.name}的性别是{self.gender}")


class Student(Man, Woman):
    def __init__(self, name, age):
        super(Student, self).__init__(name)
        self.age = age

    # def func(self):
    #     super().func()
    #     print(f"{self.name}的性别是{self.gender},年龄是{self.age}")


if __name__ == '__main__':
    stu = Student("小明", "18")
    stu.func()

结果:小明的性别是男

4.静态方法

静态方法是类中的函数,不需要实例。静态方法主要放一些逻辑性代码,主要是一些逻辑属类,但是和类本身没有什么交互,即在静态方法中,不会涉及类中方法和属性的操作。静态方法定义的时候要用@staticmethod装饰器

静态方法可以通过类名访问,也可以通过实例访问

class OperateNum(object):
    def __init__(self,num1,num2):
        self.num1 = num1
        self.num2 = num2

    @staticmethod
    def sum(a,b):
        return a+b

    def fun_sum(self):
        res = OperateNum.sum(self.num1,self.num2)
        print(res)

if __name__ == '__main__':
    test = OperateNum(1,2)
    test.fun_sum()
	print(test.sum(1,2))
结果:
3
3

静态方法不需要创建对象就可以访问类中的方法。

5.魔法方法和特殊属性

魔法方法也被称为特殊方法,总是被双下划线包围,如:_init_(),特殊属性也是以双下划线包围的属性,如:_dict_(),_slots_()。

5.1 dict

__dict__可以访问类的所有属性,以字典的方式返回

class Student(object):

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

    def func(self):
        print(f"{self.name}的性别是{self.gender},年龄是{self.age}")


if __name__ == '__main__':
    xiaoming = Student("小明", "男", 18)
    print(xiaoming.__dict__)
    print(xiaoming.__dict__['name'])
    xiaoming.func()
结果:
{'name': '小明', 'gender': '男', 'age': 18}
小明
小明的性别是男,年龄是18

5.2 slots

Python中新类增加了__slots__内置属性,可以把实例属性锁定到__slots__规定的范围内。在python中,我们创建一个类的实例后,可以给这个实例绑定任何属性和方法,这就是动态语言的灵活性。

class Student(object):
	pass

给动态实例绑定一个属性

s = Student()
s.name = 'zhangsan'
print(s.name)

如果要限制实例的属性怎么办呢?例如只对Student实例添加name和age属性。限制实例的属性就要用到__slots__特殊属性,这个属性可以用元组定义允许绑定的属性名称。只有这个元组中出现的属性可以被类实例调用。

class Student(object):
    __slots__ = ("name", "age", "gender")

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


if __name__ == '__main__':
    s = Student("张三", "18", "男")
    print(f"{s.name},{s.age},{s.gender}")
    s.score = 90
    print(s.score)
结果:
AttributeError: 'Student' object has no attribute 'score'
张三,,18

报错的原因是:score没有在__slots__中,不能绑定score属性。值得注意的是:__slots__定义的属性仅能对当前的类实例起作用,对继承它的子类是不起作用的。

class People(object):
    __slots__ = ("name", "age", "gender")

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


class Student(People):
    def __init__(self,name, gender, age):
        super(Student, self).__init__(name, gender, age)


if __name__ == '__main__':
    s = Student("张三", "18", "男")
    print(f"{s.name},{s.age},{s.gender}")
    s.score = 90
    print(s.score)
运行结果:
张三,,18
90

输出结果显示,子类的实例不受父类__slots__束缚

5.3 str()

不知道大家再写程序是,打印一个实例化对象时,打印的其实时一个对象的地址。而通过__str__()函数就可以帮助我们打印对象中具体的属性值,或者你想得到的东西。

因为再python中调用print()打印实例化对象时会调用__str__()如果__str__()中有返回值,就会打印其中的返回值。

class ss:
    def __init__(self,age,name):
        self.age = age
        self.name = name
    def __str__(self):
        return str(self.age)+",,wozenmezhemeshuai,,"+self.name
if __name__=="__main__":
    s = ss(21,'aitebao')
    print(s)
结果:
21,,wozenmezhemeshuai,,aitebao

5.4 iter()

如果想要一个类用于for…in循环,类似元组或者列表,就必须实现__iter__()方法。该方法返回一个迭代对象,python中的for循环会不断调用该迭代对象的__next__()方法,获得循环的下一个值,下面以斐波那契数列为例,写一个可以作用与for循环的Fib类。

class Fib(object):
    def __init__(self):
        self.a, self.b = 0, 1

    def __iter__(self):
        return self

    def __next__(self):
        self.a, self.b = self.b, self.a + self.b
        if self.a > 100:
            raise StopIteration
        return self.a

for n in Fib():
    print(n)
结果:
1
1
2
3
5
8
13
21
34
55
89

5.5 getitem()

Fib实例虽然可以作用于for循环,但是,不能将它作为列表来用,比如获取第四个元素。

class Fib(object):
    def __init__(self):
        self.a, self.b = 0, 1

    def __iter__(self):
        return self

    def __next__(self):
        self.a, self.b = self.b, self.a + self.b
        if self.a > 100:
            raise StopIteration
        return self.a

for n in Fib()[3]:
    print(n)
发现报错: 
for n in Fib()[3]:
TypeError: 'Fib' object is not subscriptable

如果要获取第四个元素,需要实现__getitem__()方法,代码如下:

class Fib(object):
    def __getitem__(self, item):
        a, b = 1, 1
        for x in range(item):
            a, b = b, a + b
        return a


fib = Fib()
print(fib[3])  # 结果是: 3

5.6 getattr()

如果调用类的方法或者属性时,类的方法或者属性不存在,就会报错。要避免这个错误,可以使用__getattr_()方法动态的返回一个属性。

class Student(object):
    def __init__(self, name):
        self.name = name

    def __getattr__(self, item):
        if item == 'age':
            return 18


stu = Student(name="xiaoming")
print(stu.age)
# 18

当调用不存在的属性age时,python解释器会调用__getattr__(self,‘age’)以尝试获取属性,这样就会返回age属性的值。

5.7 getattribute

因为python中所有类默认继承object类。而object类提供了了很多原始的内建属性和方法,所以用户自定义的类在Python中也会继承这些内建属性。可以使用dir()函数可以查看,虽然python提供了很多内建属性但实际开发中常用的不多。而很多系统提供的内建属性实际开发中用户都需要重写后才会使用。对于python来说,属性或者函数都可以被理解成一个属性.
— 版权声明:本文为CSDN博主「涤生大数据」的原创文章,遵循CC 4.0
BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_26442553/article/details/82467777

1.重写__getattribute__方法


class Student(object):
    country = "china"  #类属性不会放到__dict__中
    def __init__(self,name,age):
        self.name = name
        self.age = age
 
    def __getattribute__(self, attr): #注意:attr是传入的属性名,不是属性值
        print("开始属性校验拦截功能")
        print(attr)
        return object.__getattribute__(self, attr) #返回属性名
 
s1 = Student("tom",19)
print(Student.country,s1.country,s1.name,s1.age) #调用属性,会调用__getattribute__方法
 
'''注意结果,调用了四次属性,但是却只调用了3次 __getattribute__方法。
开始属性校验拦截功能
country
开始属性校验拦截功能
name
开始属性校验拦截功能
age
china china tom 19
'''

分析总结:

  1. __getattribute__是属性访问拦截器,就是当这个类的属性被实例访问时,会自动调用类的__getattribute__方法。当实例调用属性时,比如s1.name,会把name作为实参传进__getattribute__方法中,经过一系列操作后,再把name处理后的结果返回。Python中只要定义了继承object的类,就默认存在属性拦截器,只不过是拦截后没有进行任何操作,而是直接返回。所以我们可以自己改写__getattribute__方法来实现相关功能,比如查看权限、打印log日志等。
  2. 如果重写了__getattribute__,则类会调用重写的方法,所以这个方法必须要有renturn返回值,返回传进去的属性,否则调用属性会出现失败的情况。
  3. 注意如果是直接用类名.类属性的形式调用类属性,是不会调用 __getattribute__方法。

2.重写__getattribute__实现属性拦截功能

class Student(object):
    country = "china"  #类属性不会放到__dict__中
    def __init__(self,name,age):
        self.name = name
        self.age = age
 
    def __getattribute__(self, attr): #注意:attr是传入的属性名,不是属性值
        print("开始属性校验拦截功能")
        print(attr)
        if attr == "name":  #注意这里引用原属性名不用self,直接引号引起来即可。
            print("现在开始调用的是name属性")
        elif attr =="age":
            print("现在开始调用的是age属性")
        else:
            print("现在调用的是其他属性")
        return object.__getattribute__(self, attr) #返回属性名
 
s1 = Student("tom",19)
print(s1.name,s1.age,s1.country)
 
'''结果如下:
开始属性校验拦截功能
name
现在开始调用的是name属性
开始属性校验拦截功能
age
现在开始调用的是age属性
开始属性校验拦截功能
country
现在调用的是其他属性
tom 19 china
'''

5.8 getattr

如果在类中定义了这个方法,并且在该类字典中找不到相应属性的时候,会调用这个方法。

实例instance通过instance.name访问属性name,只有当属性name没有在实例的__dict__或它构造类的__dict__或基类的__dict__中没有找到,才会调用__getattr__。

当属性name可以通过正常机制追溯到时,getattr__是不会被调用的。如果在__getattr(self, attr)存在通过self.attr访问属性,会出现无限递归错误。

class ClassA(object):
    def __init__(self, classname):
        self.classname = classname
 
    def __getattr__(self, attr):
        return('invoke __getattr__', attr)
insA = ClassA('ClassAname')
 
print(insA.__dict__) # 实例insA已经有classname属性了
# {'classname': 'ClassA'}
 
print(insA.classname) # 不会调用__getattr__
# ClassA
 
print(insA.grade) # grade属性没有找到,调用__getattr__
# ('invoke __getattr__', 'grade')
 
print(insA.__dict__) # 实例insA已经有classname属性了
# {'classname': 'ClassA'}
 
#***********************************************************************
print(getattr(insA,'classname'))
#ClassAname
 
print(getattr(insA,'grade'))
# ('invoke __getattr__', 'grade')
 
setattr(insA, "grade", "18")              #增加属性
 
print(insA.__dict__)
#{'classname': 'ClassAname', 'grade': '18'}
 
print(insA.grade)
#18

5.9 setattr

setattr:无论是直接赋值还是通过内置的setattr函数赋值,都会调用。。。基类一般会内置__setattr__()方法

如果类自定义了__setattr__方法,当通过实例获取属性尝试赋值时,就会调用__setattr__。

常规的对实例属性赋值,被赋值的属性和值会存入实例属性字典__dict__中。

class ClassA(object):
    def __init__(self, classname):
        self.classname = classname
 
insA = ClassA('ClassA')
 
print(insA.__dict__)
# {'classname': 'ClassA'}
 
insA.tag = 'insA'             #增加属性,正常
 
print(insA.__dict__)
# {'tag': 'insA', 'classname': 'ClassA'}
 
#********************************************************
setattr(insA, "age", "18")     #增加属性,正常
 
print(insA.__dict__)
#{'tag': 'insA', 'classname': 'ClassA', 'age': '18'}
 
#********************************************************
#显式调用
insA.__setattr__('tag1','insA1')
print(insA.__dict__)
#{'tag1': 'insA1', 'tag': 'insA', 'classname': 'ClassA', 'age': '18'}
 
#********************************************************
#从基类继承了__setattr__()方法
print(dir(insA))
#['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__',
#'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__',
#'__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
#'__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age',
#'classname', 'tag', 'tag1']

如下类自定义了__setattr__,对实例属性的赋值就会调用它。类定义中的self.attr也同样,所以在__setattr__下还有self.attr的赋值操作就会出现无线递归的调用__setattr__的情况。自己实现__setattr__有很大风险,一般情况都还是继承object类的__setattr__方法。 __ setattr __:无论是直接赋值还是通过内置的setattr函数赋值,都会调用。

class ClassA(object):
    def __init__(self, classname):
        self.classname = classname
 
    def __setattr__(self, name, value):
        # self.name = value  # 如果还这样调用会出现无限递归的情况
        print('invoke __setattr__')
 
insA = ClassA('ClassAname') # __init__中的self.classname调用__setattr__。
# invoke __setattr__
 
print(insA.__dict__)
# {}                            #注意这里
 
insA.tag = 'insA'    
# invoke __setattr__
 
print(insA.__dict__)
# {}
 
#********************************************************
setattr(insA, "age", "18") 
#invoke __setattr__
 
print(insA.__dict__)
# {}

5.10 delattr

与__setattr __()类似,但用于删除属性而不是赋值。只有当del obj.name对对象有意义时才应该实现。

6.可调用对象

在python中函数也是一个对象。实际上,任何一个有__call__()特殊方法的对象都是函数,示例如下:

class Add(object):
    def __call__(self, *args):
        return sum(*args)


add = Add()	# 定义函数对象,可以把函数对象看作函数
print(add([1,2,3,4]))  # 调用函数,结果为:10

所有的函数都是可调用函数。由于add可以调用,所以add被称为可调用对象。
一个实例也可以变成一个可调用对象,只需要实现__call__()方法,比如把People类变成一个可调用对象。

class Person(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __call__(self, friend):
        print(self.name, self.age)
        print(friend)


if __name__ == '__main__':
    p = Person("小明", 18)
    p("张三")
结果:
小明 18
张三

仅仅看p(“张三”)无法确定Person是一个函数还是一个类实例,所以在python中,函数也是对象,对象和函数区别不显著,这也是python灵活的地方。
那么如何判断一个变量是函数还是对象呢?
很多时候判断一个对象能否被调用,可以使用callable()函数。

print(callable(p))			# True
print(callable(["a"]))		# False
print(callable(max))		# True

由输出结果可以知道,通过callable()函数可以判断一个对象是否是可调用对象。

7.补充Python内置类属性

在这里插入图片描述

#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
class Employee:
   '所有员工的基类'
   empCount = 0
 
   def __init__(self, name, salary):
      self.name = name
      self.salary = salary
      Employee.empCount += 1
   
   def displayCount(self):
     print "Total Employee %d" % Employee.empCount
 
   def displayEmployee(self):
      print "Name : ", self.name,  ", Salary: ", self.salary
 
print "Employee.__doc__:", Employee.__doc__
print "Employee.__name__:", Employee.__name__
print "Employee.__module__:", Employee.__module__
print "Employee.__bases__:", Employee.__bases__
print "Employee.__dict__:", Employee.__dict__

执行结果如下:

Employee.__doc__: 所有员工的基类
Employee.__name__: Employee
Employee.__module__: __main__
Employee.__bases__: ()
Employee.__dict__: {'__module__': '__main__', 'displayCount': <function displayCount at 0x10a939c80>, 'empCount': 0, 'displayEmployee': <function displayEmployee at 0x10a93caa0>, '__doc__': '\xe6\x89\x80\xe6\x9c\x89\xe5\x91\x98\xe5\xb7\xa5\xe7\x9a\x84\xe5\x9f\xba\xe7\xb1\xbb', '__init__': <function __init__ at 0x10a939578>}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

m 宽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值