Python—OOP

  • python是面向对象(每个对象包含标识、类型、值的信息)的,对象的本质就是:一个内存块,拥有特定的值,支持特定类型的相关操作 。变量:对象的引用。
  • Python 中“一切皆对象”, 所有的赋值操作都是“引用的赋值”

OOP(Object-oriented programming )面向对象编程将数据和操作数据相关的方法封装到对象中,组织代码和数据的方式更加接近人的思维,从而大大提高了编程的效率。

小甲鱼视频


简介

Tips操作:

  •  dir() :获得一个对象的所有属性和方法,,它返回一个包含字符串的list;

常用的概念

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

 

 

 

基础概念

对象的进化

  1. 简单数据:比如30.4,20等等(包含了加法、乘法等方法的对象);
  2. 数组:将同类型的数据放到一起。比如:整数数组[20,30,40],浮点数数组[10.2, 11.3, 12.4],字符串数组:["aa", "bb", "cc"]
  3. 结构体:将不同类型的数据放到一起,是C 语言中的数据结构(labview中的簇Cluster,python中的tuple)。比如:
    struct resume{
             int age;
             char name[10];
             double salary;
              };
  4. 对象:将不同类型的数据、方法(即函数)放到一起,就是对象。相比结构体,对象不仅有数据,还有处理数据的方法。

一个Python对象包含如下部分:

  • id (identity 识别码)
  • type (对象类型)
  • value (对象的值)
    • 属性 (attribute)
    • 方法 (method)

 

类对象、类属性和类方法

类对象(class object):当解释器执行class 语句时("class 类名:"),就会创建一个类对象。

  • 两个数据成员——属性(类属性、实例属性)和方法(类方法、实例方法);
  • 属性 ⇔ 变量方法 ⇔ 函数
  • 属性+方法=对象;
  • 我们通过类定义数据类型的属性(数据)和方法(行为),也就是说,"类将行为和状态打包在一起";
  • 对象是类的具体实体,一般称为"类的实例",类看做"饼干模具",对象就是根据这个"模具"制造出的"饼干";
  • 从一个类创建对象时,每个对象会共享这个类的行为(类中定义的方法),但会有自己的属性值(不共享状态),更具体一点:"方法代码是共享的,属性数据不共享";
  • Python 中一切皆对象,类也称为"类对象",类的实例也称为"实例对象";

 

# coding: gbk
class Student: # 类名一般首字母大写,多个单词采用驼峰原则
#class Student默认是class Student(object)
    pass #空语句

print(type(Student))
# 输出<class 'type'>,类也是对象
print(id(Student))
# 输出2350587049000


Stu2 = Student # 指向相同的内存地址
s1 = Stu2() # 实例化对象
print(id(s1))
# 输出2350594853408
print(s1)
# 输出<__main__.Student object at 0x000002234A5F0A20>

对于一个实例化对象obj,print(obj) 打印的是对象的字符串表示形式,通常是 <__main__.ClassName object at 0x0000000002B5FDD8> 这样的形式,其中 0x00000002B5FDD8 是对象在内存中的地址。而 print(id(obj)) 打印的是对象的唯一标识符,它是一个整数值,用于标识对象在内存中的位置。在Python中,每个对象都有一个唯一的ID,可以使用 id() 函数获取它。因此, print(obj)  print(id(obj)) 打印的信息不同,但都与对象在内存中的位置有关。

在上面的例子中000002234A5F0A20十六进制转为10进制即得到2350594853408。

注:pass 为空语句。就是表示什么都不做,只是作为一个占位符存在。当你写代码时,遇到暂时不知道往方法或者类中加入什么时,可以先用 pass 占位,后期再补上。

类属性(class attributes)是从属于"类对象"(class object)的属性,也称为"类变量"。由于类属性从属于类对象,可以被所有实例对象共享。

类属性的定义方式:
                                class  类名:
                                         类变量名 =  初始值

在类中或类的外面,我们可以通过:类名.类变量名来读写。

可以用类方法(class method)操作类属性。类方法中访问实例属性和实例方法会导致错误;子类继承父类方法时,传入cls是子类对象,而非父类对象。

# coding: gbk
class Student:
    company = "SXT"  # 类属性
    @classmethod  # 类方性,该句必须位于类方法的上一行
    def printCompanys(cls,number_1,number_2): # 类方法名(cls [,形参列表])
        print(cls.company)
        print(number_1 + number_2)

Student.printCompanys(2,3)
# 输出 SXT 5

 

实例属性、实例方法、内存

实例属性(instance attributes):从属于实例对象的属性。

  • 实例属性一般在__init__()方法中通过如下代码定义:self.实例属性名 = 初始值
  • 在本类的其他实例方法中,也是通过 self 进行访问:self.实例属性名
  • 创建实例对象后,通过实例对象访问:
    • obj01 = 类名() #创建对象,调用__init__()初始化属性
    • obj01.实例属性名 = 值 #可以给已有属性赋值,也可以新加属性

实例方法(instance method):操作实例操作实例属性。实例方法的定义格式如下:

  • 定义实例方法时,第一个参数必须为 self。和前面一样,self 指当前的实例对象。
  • 调用实例方法时,不需要也不能给 self 传参。self 由解释器自动传参。
  • 方法调用时,通过对象来调用。方法从属于特定实例对象,普通函数没有这个特点。
  • 直观上看,方法定义时需要传递 self,函数不需要。

__init__方法、__new__方法和self

  • __init__方法:初始化创建好的对象,初始化指的是给实例属性赋值;
  • __new__方法:用于创建对象,但我们一般无需重定义该方法;
  • 如果我们不定义__init__方法,系统会提供一个默认的__init__方法,如果我们定义了带参的__init__方法,系统不创建默认的__init__方法;
  • __init__(self, argus)第一个参数固定为self,self指的就是刚刚创建好的实例对象,它可以在每个方法中使用,因为每个方法都有self;
  • Python中的 self相当于C++中的 self指针,JAVA和C#中的this关键字。Python中,self 必须为构造函数的第一个参数,名字可以任意修改。但一般遵守惯例,都叫做self。
  • 类相当于图纸,类实例化后的对象才是能住人的房子。想一下如果有一排房子都是同一个图纸设计出来的,那么我们可以说这些房子属于同一个class类,但是房子的门牌号都不同,self就相当于门牌号码,用来区分每一个房子。
  • Python面向对象的self究竟是什么?

 

# coding: gbk
class Student:

    company = "尚学堂"  #类属性
    count = 0        #类属性

    def __init__(self,name,score):# __int__表示构造方法,self必须位于第一个参数
        self.name = name  # 实例属性,传递参数,self表示当前对象本身
        self.score =score
        Student.count = Student.count+1 #计数器,每创建一个对象+1

    def say_score(self):  # 实例方法,self必须位于第一个参数
        print("我的公司是:",Student.company)
        print(self.name,"的分数是",self.score)
        #print("{0}的分数是:{1}".format(self.name,self.score))

aa = Student("高淇",18) # aa是实例对象,自动调用__init__()方法
aa.say_score()
# 输出: 我的公司是 尚学堂 高淇 的分数是 18
Student.say_score(aa)
# 输出: 我的公司是 尚学堂 高淇 的分数是 18
aa.salary = 10 #专门为aa添加新的属性-salary
print(aa.salary) # 输出: 10
print(dir(aa)) # 对象aa的所有属性、方法
print(aa.__dict__) #定义的属性字典
print(isinstance(aa,Student))
# 输出True,判断对象为aa是否为Student的实例对象

内存分析

 

其他

静态方法(static method):与类对象无关的方法。静态方法和在模块中定义的普通函数没有区别,只不过静态方法放到了类的名字空间里,需要通过类调用。

# coding: gbk
class Student:

    company = "尚学堂"  #类属性
    count = 0        #类属性

    @staticmethod # 必须位于静态方法上面一行
    def add(a,b): # 静态方法
        print("{0}+{1}={2}".format(a,b,(a+b)))
        return a+b

Student.add(20,30) # 输出 20+30=50

私有属性和私有方法

  • Python 对于类的成员没有严格的访问控制限制,这与其他面向对象语言有区别;
  • 通常我们约定,两个下划线开头的属性是私有的(private)。其他为公共的(public);
  • .类内部可以访问私有属性(方法);
  • 类外部不能直接访问私有属性(方法);
  • . 类外部可以通过“_类名__私有属性(方法)名”访问私有属性(方法)
# coding: gbk
class Demo:
    def __init__(self, name, uage):
        self.uname = name
        self.__uage = uage # 私有属性

    def __age(self): # 私有方法
        print("他的年纪是",self.__uage) #类内直接访问私有属性

d = Demo('Tom', 18)
print(d._Demo__uage)  # 访问私有属性
d._Demo__age()  # 调用私有方法

print(d.uage)  # 直接方法私有属性,报错
print(d.Demo.uage) # 错误的方法访问私有属性,报错

类编码风格

  • 类名首字母大写,多个单词之间采用驼峰原则。
  • 实例名、模块名采用小写,多个单词之间采用下划线隔开。
  • 每个类,应紧跟“文档字符串”,说明这个类的作用。
  • 可以用空行组织代码,但不能滥用。在类中,使用一个空行隔开方法;模块中,使用两个空行隔开多个类。

可能待补充:

  • __del__方法(析构函数)和垃圾回收机制
  • __call__方法和可调用对象
  • 方法没有重载
  • 私有属性和私有方法(实现封装)
  • @property 装饰器
  • 属性和方法命名总结

 

进阶

继承

  • 继承可以让子类具有父类的特性,提高了代码的重用性;
  • 从设计上是一种增量进化,原有父类设计不变的情况下,可以增加新的功能,或者改进已有的算法;
  • 一个子类可以继承多个父类;
  • 如果在类定义中没有指定父类,则默认父类是object类,也就是说,object是所有类的父类,里面定义了一些所有类共有的默认实现,比如:__new__()
  • 通过类的方法 mro()或者类的属性__mro__可以输出这个类的继承层次结构;

 

# coding: gbk
class Person:

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

    def say_age(self):
        print(self.name,"的年龄是: ",self.__age)

class Student(Person):

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

s1 = Student("张三",18,85)
s1.say_age()  # 输出:张三 的年龄是:  18
print(s1._Person__age) # 访问父类的私有属性,输出18
print(dir(s1)) #查看s1的所有属性方法
print(Student.mro()) # Method Resolution Order
# [<class '__main__.Student'>, <class '__main__.Person'>, <class 'object'>]

 

x

封装(隐藏)

隐藏对象的属性和实现细节,只对外提供必要的方法。相当于将“细节封装起来”,只对外暴露"相关调用方法”。

通过前面学习的"私有属性、私有方法"的方式,实现“封装”。Python 追求简洁的语法,没有严格的语法级别的"访问控制符",更多的是依靠程序员自觉实现。

补充私有属性、私有方法

list是python的序列对象。list就是对象,它提供了若干种方法来让我们根据需求来调整整个列表(比如append,sort),但是我们不知道list对象里面的那些方法是如何实现的,也不知道list对象里面有哪些变量,这就是封装,只给我们需要的方法的名字。

 

多态

多态(polymorphism)是指同一个方法调用由于对象不同可能会产生不同的行为。在现实生活中,我们有很多例子。比如:同样是调用人的休息方法,张三的休息是睡觉,李四的休息是玩游戏,高淇老师是敲代码。同样是吃饭的方法,中国人用筷子吃饭,英国人用刀叉吃饭,印度人用手吃饭。 关于多态要注意以下 2 点: 1. 多态是方法的多态,属性没有多态。 2. 多态的存在有 2 个必要条件:继承、方法重写。

补充下面例子isinstance的介绍

 

# coding: gbk
# 多态
class Animal:
    def shout(self):
        print("动物叫了一声")

class Dog(Animal):
    def shout(self):
        print("小狗,汪汪汪")

class Cat(Animal):
    def shout(self):
        print("小猫,喵喵喵")


def animalShout(a):
    if isinstance(a, Animal):
        a.shout()  # 传入的对象不同,shout方法对应的实际行动也不同

animalShout(Dog())  # 输出:小狗,汪汪汪
animalShout(Cat())  # 输出:小猫,喵喵喵


### 以下没有继承,所以不是多态
class A:
    def fun(self):
        print("我是小A")

class B:
    def fun(self):
        print("我是小B")

a = A()
b = B()
a.fun()  #输出我是小A
b.fun()  #输出我是小B,虽然函数名都是fun

 

材料科学(化学)中的多态:图中的材料都是C单质形成的物质,但是结构和性质不同,因而是不同的物质。
Some allotropes of carbon: 按顺序依次为:

  • diamond
  • graphite
  • lonsdaleite
  • C60
  • C540
  • C70
  • amorphous carbon
  • carbon nanotube

上面展示的其实就是C的同素异形体,从更大的概念上说,上面说的多态其实是同质异形体(polymorphism),比如Al2O3的多种晶型。进一步类比,OOP的多态前提是继承,其实就是保证我们这里化学组成相同,比如α-Al2O3和β-Al2O3都是继承自Al2O3,那么如果都调用space_group方法,得到的结果肯定不同。

 

仪器控制

海洋光学

注:这里给出了usb4000.py的驱动,可以学习一下,可能可以用pyvisa模块实现控制(类似labview)。

按照python-seabreeze以及对应的documentation的要求,安装相应的模块。我是在conda环境下运行的,python-seabreeze 默认为 cseabreeze 后端,于是可能存在找不到光谱仪的情况,参考github-#47。我的运行环境是pycharm-conda-python3.11。

实例-1-找到ocean optics设备

# coding: gbk
# 修改前
import seabreeze.spectrometers as sb
devices = sb.list_devices()
print(devices)  # 无法找到设备,输出空[]

#%%
# 修改后
import seabreeze
seabreeze.use("pyseabreeze")
import seabreeze.spectrometers as sb
devices = sb.list_devices()
print(devices)  # 能正确找到设备,输出 [<SeaBreezeDevice HR2000PLUS:HR+C0731>]

 

实例-2-光谱动态测量

可用,来自OceanOptics_spectrometer,我在我的环境下运行会出现问题:只有第一个图能正常测量,然后后面不断出新的图,但是没有任何内容。
解决办法:Pycharm-File-Setting-搜索"Show plots in tool window",选择不勾选。

import matplotlib.pyplot as plt
import numpy as np
import time

import seabreeze
seabreeze.use("pyseabreeze")
from seabreeze.spectrometers import Spectrometer, list_devices


devices = list_devices()
print(devices)
spec = Spectrometer(devices[0])

# Set integration time if needed
int_time = spec.integration_time_micros(10000)

wavelengths = spec.wavelengths()
intensities = spec.intensities()

# data = np.column_stack((wavelengths, intensities))
# fname = "bg_spectrum.txt"
# np.savetxt(fname, data, delimiter='\t')

# bg_data = np.loadtxt('bg_spectrum.txt')
# bg_signal = bg_data[:,1]

fig, ax = plt.subplots()
while True:
    intensities = spec.intensities()
    # intensities = intensities-bg_signal
    ax.clear()
    ax.plot(wavelengths,intensities,'r')
    ax.set_xlabel('Wavelength (nm)')
    ax.set_ylabel('Intensity (arb.unit)')
    ax.set_xlim(200,1100)
    # ax.set_ylim(0,7000)
    plt.grid()
    plt.pause(0.02)
    
    if plt.waitforbuttonpress(timeout=0.1):
        break

plt.show()

 

实例-3-光谱动态测量+保存

数据保存格式:第一列是测试的时间,第n行的[2:end]表示的是光谱强度。

# coding: gbk

import matplotlib.pyplot as plt
import numpy as np
import time

import seabreeze
seabreeze.use("pyseabreeze")
from seabreeze.spectrometers import Spectrometer, list_devices

devices = list_devices()
print(devices)
spec = Spectrometer(devices[0])

# Set integration time if needed
int_time = spec.integration_time_micros(500000)

wavelengths = spec.wavelengths()
intensities = spec.intensities()

# 初始化数据列表
data_list = []

fig, ax = plt.subplots()
while True:
    intensities = spec.intensities()
    data_list.append(intensities.tolist())

    ax.clear()
    ax.plot(wavelengths, intensities, 'r')
    ax.set_xlabel('Wavelength (nm)')
    ax.set_ylabel('Intensity (arb.unit)')
    ax.set_xlim(200, 1100)
    plt.grid()
    plt.pause(0.1)

    # 获取当前时间
    current_time = time.strftime("%H:%M:%S", time.localtime())
    data = [current_time, ' '.join(map(str, intensities))]

    # 将数据保存到文件
    with open("data.txt", "a") as file:
        file.write(' '.join(map(str, data))+ "\n")

    if plt.waitforbuttonpress(timeout=0.1):
        break

plt.show()

 

 

 

 

Linkam冷热台T-96

利用python实现对T96的控制

前置步骤:从ZEISS下载MTB 2011,安装后即可然后找到LinkamSDK.dll和Linkam.lsk,然后根据github上的pylinkam项目的操作来。

第一版:存在的问题,温度控制和温度测试不是像labview那样的多线程multithreading。

import pandas as pd
import logging
import time
from pylinkam import interface, sdk
import matplotlib.pyplot as plt

logging.basicConfig(level=logging.DEBUG)

# 读取Excel文件
df = pd.read_excel('parameters.xlsx')

fig, ax = plt.subplots()

with sdk.SDKWrapper() as handle:
    with handle.connect() as connection:
        print(f"Name: {connection.get_controller_name()}")
        print(f"Heater measurement: {connection.get_value(interface.StageValueType.HEATER1_TEMP)}")
        print(f"Heater set-point before: {connection.get_value(interface.StageValueType.HEATER_SETPOINT)}")
        time_aa = time.time()
        for index, row in df.iterrows():
            target_temperature = row['目标温度(摄氏度)']
            ramp_rate = row['升温速率(度/min)']
            hold_time = row['保温时间(min)']

            # 设置目标温度为target_temperature
            if not connection.set_value(interface.StageValueType.HEATER_SETPOINT, target_temperature):
                raise Exception('Something broke')
            connection.enable_heater(True)
            # 设置升温速率为ramp_rate
            if not connection.set_value(interface.StageValueType.HEATER_RATE, ramp_rate):
                raise Exception('Something broke')
            connection.enable_heater(True)

            # 初始化x和y轴数据
            x_data = []
            y_data = []

            # 在target_temperature保持hold_time
            start_time = time.time()
            while time.time() - start_time < hold_time * 60:
                # 每隔1秒测一次实际温度
                current_temperature = connection.get_value(interface.StageValueType.HEATER1_TEMP)
                x_data.append(time.time() - time_aa)
                y_data.append(current_temperature)
                time.sleep(1)

                # 实时显示温度和时间的x-y图
                ax.plot(x_data, y_data)
                plt.xlabel('Time (Seconds)')
                plt.ylabel('Temperature (Celsius)')
                plt.title(f'Temperature Profile for {target_temperature}°C')
                plt.draw()
                plt.pause(0.01)

            connection.enable_heater(True)

    print("Temperature has reached the target")

plt.show()

 

第二版:尝试多线程,失败,保温时间用RAMP_HOLD_TIME

import pandas as pd
import logging
import time
from pylinkam import interface, sdk
import matplotlib.pyplot as plt
import threading

logging.basicConfig(level=logging.DEBUG)

# 读取Excel文件
df = pd.read_excel('parameters.xlsx')

class TemperatureControlThread(threading.Thread):
    def __init__(self, target_temperature, ramp_rate, hold_time, connection):
        threading.Thread.__init__(self)
        self.target_temperature = target_temperature
        self.ramp_rate = ramp_rate
        self.hold_time = hold_time
        self.connection = connection

    def run(self):
        # 设置目标温度为target_temperature
        if not self.connection.set_value(interface.StageValueType.HEATER_SETPOINT, self.target_temperature):
            raise Exception('Something broke')
        # 设置升温速率为ramp_rate
        if not self.connection.set_value(interface.StageValueType.HEATER_RATE, self.ramp_rate):
            raise Exception('Something broke')
        # 设置保温时间为hold_time
        if not self.connection.set_value(interface.StageValueType.RAMP_HOLD_TIME, self.hold_time):
            raise Exception('Something broke')
        # 启动加热器
        self.connection.enable_heater(True)
        # 等待保温结束
        time.sleep(self.hold_time * 60)
        # 关闭加热器
        self.connection.enable_heater(False)

class TemperatureMeasurementThread(threading.Thread):
    def __init__(self, connection):
        threading.Thread.__init__(self)
        self.connection = connection
        self.x_data = []
        self.y_data = []

    def run(self):
        while True:
            # 每隔1秒测一次实际温度
            current_temperature = self.connection.get_value(interface.StageValueType.HEATER1_TEMP)
            self.x_data.append(time.time())
            self.y_data.append(current_temperature)
            time.sleep(1)

    def get_data(self):
        return self.x_data, self.y_data

with sdk.SDKWrapper() as handle:
    with handle.connect() as connection:
        print(f"Name: {connection.get_controller_name()}")
        print(f"Heater measurement: {connection.get_value(interface.StageValueType.HEATER1_TEMP)}")
        print(f"Heater set-point before: {connection.get_value(interface.StageValueType.HEATER_SETPOINT)}")

        # 创建温度控制线程
        control_threads = []
        for index, row in df.iterrows():
            target_temperature = row['目标温度(摄氏度)']
            ramp_rate = row['升温速率(度/min)']
            hold_time = row['保温时间(min)']
            control_thread = TemperatureControlThread(target_temperature, ramp_rate, hold_time, connection)
            control_threads.append(control_thread)

        # 创建温度测量线程
        measurement_thread = TemperatureMeasurementThread(connection)
        measurement_thread.start()

        # 启动温度控制线程
        for control_thread in control_threads:
            control_thread.start()

        # 实时显示温度和时间的x-y图
        while True:
            x_data, y_data = measurement_thread.get_data()
            plt.plot(x_data, y_data)
            plt.xlabel('Time (Seconds)')
            plt.ylabel('Temperature (Celsius)')
            plt.title('Temperature Profile')
            plt.draw()
            plt.pause(0.01)

plt.show()

 

 

 

 

Leave a Reply