同一类的不同实例的后续副本上的deepcopy时间加倍

2024-06-01 08:10:52 发布

您现在位置:Python中文网/ 问答频道 /正文

在类的实例上从Python copy模块运行deepcopy时,复制过程的时间随着每次迭代而加倍,即使实例都属于同一个类,因此(至少在我看来)复制所需的时间应该是相同的。你知道吗

我复制的对象使用kwargs来设置一组属性。它最初还调用了__init__中的set_dynamic_attributes方法,以根据某些逻辑配置某些属性,但为了测试这是否导致了速度减慢,我从__init__中删除了调用,并专门调用了它。你知道吗

以下是违规部分,以及结果:

for a in base_wpn.base_attacks:
    t0 = time.time()
    a.set_dynamic_attributes()
    t1 = time.time()
    print('Dyn attributes set time: ' + str(t1-t0))
    atk = deepcopy(a)
    t2 = time.time()
    print('Deepcopy time: ' + str(t2-t1))

结果:

Dyn attributes set time: 0.0
Deepcopy time: 0.01399993896484375
Dyn attributes set time: 0.0
Deepcopy time: 0.03599882125854492
Dyn attributes set time: 0.0
Deepcopy time: 0.04999995231628418
Dyn attributes set time: 0.0
Deepcopy time: 0.09999823570251465
Dyn attributes set time: 0.0
Deepcopy time: 0.011002540588378906
Dyn attributes set time: 0.0
Deepcopy time: 0.021996021270751953
Dyn attributes set time: 0.0
Deepcopy time: 0.0429990291595459
Dyn attributes set time: 0.0
Deepcopy time: 0.08499836921691895
Dyn attributes set time: 0.0
Deepcopy time: 0.17699956893920898
Dyn attributes set time: 0.0
Deepcopy time: 0.32700061798095703
Dyn attributes set time: 0.0
Deepcopy time: 0.6589939594268799
Dyn attributes set time: 0.0
Deepcopy time: 1.4200007915496826
Dyn attributes set time: 0.0
Deepcopy time: 2.466003656387329
Dyn attributes set time: 0.0
Deepcopy time: 5.228000164031982
Dyn attributes set time: 0.0
Deepcopy time: 10.528998374938965

如果有帮助的话(我知道如果没有更多的代码和大量的对象引用,它是不可复制的),下面是构建实例的对象定义:

class Attack():
    def __init__(self, name, weapon, **kwargs):
        self.name = name
        self.weapon = weapon
        self.skill = [self.weapon.skill]
        self.attack_mod = self.weapon.attack_mod
        self.parry_mod = -self.weapon.parry_mod #Modifier to OPPONENT'S parry chance
        self.stamina = self.weapon.stamina
        self.main_shape = None
        self.striker = None
        self.hands = 1
        self.damage_type = 'b'
        self.base_ap = 0
        self.hand = True
        self.added_mass = self.weapon.added_mass
        self.length = 0 #Used to add or subtract from base weapon length got added/reduced reach
        self.side_restrict = True #Determines if the attack can only hit one side of the enemy (i.e. hook from R hand only hitting left side)
        self.restricted_locs = [] #Locations that can never be targeted with this attack (i.e. foot with uppercut)
        self.allowed_angles_r = [] #Angles that are allowed as an index of angles (0 = N-> S, 7 = NW -> SE, 8 = thrust) (i.e. N->S with an uppercut)
        self.allowed_angles_l = [] #Angles that are allowed as an index of angles (0 = N-> S, 7 = NW -> SE, 8 = thrust) (i.e. N->S with an uppercut)
        self.main_area = 0
        self.mech_adv = 0
        self.force_scalar = 1 #Used to adjust force/damage for the attack

        for k in kwargs:
            for key in self.__dict__:
                if k == key:
                    self.__dict__.update(kwargs)

        #self.set_dynamic_attributes()

    def set_dynamic_attributes(self):

        for t in self.damage_type:
            if t == 'b':
                if self.striker == 'main':
                    shape = self.weapon.main_shape
                else:
                    shape = 'round'
                if shape == 'wedge':
                    self.main_area = self.weapon.main_length * self.weapon.avg_main_width
                    self.mech_adv =  self.weapon.main_depth / self.weapon.main_width
                elif shape == 'round':
                    #Using Hertz's result, but using fixed f value built off of added_mass + fist mass (.86) and v of 40 f/s2 and fixed p_ratio for target
                    #Equation: https://www.quora.com/Is-the-area-of-contact-between-a-cylinder-and-a-flat-surface-infinitely-small-Is-it-a-point
                    if self.striker == 'main':
                        material = self.weapon.main_material
                        width = self.weapon.main_width
                        length = self.weapon.main_length
                    elif self.striker == 'shaft':
                        material == self.weapon.shaft_material
                        width = 1
                        length = self.weapon.shaft_length
                    else:
                        material = self.weapon.accent_material
                        width = length = 1
                    e_calc = ((1-(material.p_ratio * material.p_ratio))/(material.elasticity*10))+((1-(.4*.4))/5)
                    self.main_area = sqrt((4*((.86 + self.added_mass)*40)*width)/(3.514*(e_calc)*min(length, 8)))
                elif self.main_shape == 'flat':
                    self.main_area = min(self.weapon.main_length,8) * min(self.weapon.main_width,8)

            elif t == 's':
                if self.main_shape == 'blade':
                    self.main_area = min(self.weapon.main_length, 8) * self.weapon.avg_main_width
                    self.mech_adv =  self.weapon.main_depth / self.weapon.main_width
                elif self.main_shape == 'de blade':
                    self.main_area = min(self.weapon.main_length, 8) * self.weapon.avg_main_width
                    self.mech_adv =  (self.weapon.main_depth/2) / self.weapon.main_width

            elif t == 'p':
                if self.striker == 'main':
                    shape = self.weapon.main_shape
                    length = self.weapon.main_length
                    depth = self.weapon.main_depth
                    width = self.weapon.main_width
                else:
                    shape = 'point'
                    if self.striker == 'shaft':
                        length = min(self.weapon.shaft_length, 8)
                        depth = width = 1
                    else:
                        length = depth = width = 1
                if shape in ['point', 'blade']:
                    wedge1 = length / width
                    wedge2 = width / depth
                    #Double each (since there are two wedges per side) and multiply for full MA of tip
                    self.mech_adv = (wedge1*2)*(wedge2*2)
                    self.main_area = depth * length * width

            else:
                if self.main_shape == 'hook':
                    wedge1 = self.weapon.main_length / self.weapon.main_width
                    wedge2 = self.weapon.main_width / self.weapon.main_depth
                    #Double each (since there are two wedges per side) and multiply for full MA of tip
                    self.mech_adv = (wedge1*2)*(wedge2*2)
                    self.main_area = self.weapon.main_depth * self.weapon.main_width



        if self.damage_type in ['s','b']:
            self.stamina += self.weapon.weight + int(self.weapon.weight*self.weapon.axis_vs_com)
            self.base_ap += min(self.weapon.weight/10, (self.weapon.weight * 10)/self.weapon.axis_vs_com)
            self.added_mass = self.weapon.weight / self.weapon.com_perc
            self.attack_mod += (20 - ((self.weapon.weight*10) * self.weapon.com_perc))
            self.parry_mod -= ((self.weapon.weight*10) * self.weapon.com_perc)

            if self.weapon.main_num > 1:
                self.attack_mod += self.weapon.main_num * 5
                self.parry_mod -= self.weapon.main_num * 20
        else:
            self.stamina += self.weapon.weight/2
            self.base_ap += self.weapon.weight * 5
            self.added_mass = self.weapon.weight/10

            if self.damage_type == 'p':
                self.attack_mod -= self.weapon.weight/10
                self.parry_mod -= self.weapon.weight * 5
            else:
                self.attack_mod += -5 + (self.weapon.main_num * 5)
                self.parry_mod -= self.weapon.main_num * 20

需要更多的代码。以下是武器类(攻击引用)和材料类(武器引用)的代码:

class Weapon:
    def __init__(self, **kwargs):
        self.name = ''
        self.shafted = False #Used to determine if wpn has a shaft
        self.allowed_main_materials = [] # List of materials applicable for the main surface. Young's modulus prevents copper and bronze swords longer than 24", for example
        self.main_material = m_steel #Damage component (blade, head, etc) material
        self.shaft_material = m_wood
        self.grip_material = m_leather
        self.accent_material = m_steel
        self.attack_mod = 0
        self.parry_mod = 0 #Mod to weilder's ability to parry with weapon
        self.b_striker = 'accent' #Striking surface for damage type. Can be main, shaft, accent, or none
        self.s_striker = 'main'
        self.p_striker = 'main'
        self.t_striker = 'none'
        self.hands = [1] #List can include 0,1,2
        self.quality = 'Average'
        self.base_name = 'Weapon'
        self.bname_variants = [] #A list of variant names for the weapon
        self.skill = None #This is the default skill used for the weapon. String
        self.length = 1
        self.shaft_length = 0 #Also used as tethers for flail and whip like weapons
        self.shaft_diameter = 0
        self.shaft_num = 0
        self.pre_load = False #Used to account for weapons that can be preloaded with velocity, like flails or staves
        self.avg_main_width = 1 #1.25 average longsword
        self.main_width = 1 #Absolute width at widest point
        self.avg_main_depth = .1 #.14 is average for a sword blade
        self.main_depth = .2 #Absolute depth at deepest point
        self.main_shape = 'de blade' #Acceptable values: de blade, blade, point, wedge, round, flat, hook
        self.main_num = 1 #Number of main attack surfaces, mostly used for flails/flogs
        self.accent_cuin = 1 #Cubic inches of accent material, such as the crossguard and pommel on a sword
        self.main_com = .5 #Center of mass for the main weapon component
        self.main_loc = .1 #Location along the total length for the main weapon component
        self.accent_loc = .05 #Location along the total length for the accent component
        self.grip_loc = .03 #location along the total length for the grip
        self.main_weight = 0
        self.shaft_weight = 0
        self.accent_weight = 0
        self.grip_weight = 0
        self.weight = 0
        self.main_length = 0
        self.added_mass = 0

        self.damage_type = 'b'
        self.stamina = 0




        #Maximums; used to procedurally gen weapons
        self.main_len_range = (0,0) #Tuple containing min and max range for acceptable lengths
        self.main_depth_range = (0,0)
        self.main_avg_depth_range = (0,0)
        self.main_width_range = (0,0)
        self.main_avg_width_range = (0,0)
        self.length_range = (0,0)
        self.shaft_length_range = (0,0) 
        self.shaft_diameter_range = (0,0)
        self.max_main_num = 1
        self.max_shaft_num = 1


        self.main_only_com = 0
        self.shaft_only_com = 0
        self.accent_only_com = 0
        self.com = 0 #Center of mass for the whole weapon
        self.com_perc = 0 #com as a percentage
        self.axis_vs_com = 0 #Shows COM relative to grip location (axis for swings). Used to determine AP/stamina costs.

        self.main_hits = 0
        self.staff_hits = 0
        self.accent_hits = 0        

        self.min_pwr_1h = 0 #1 pwr = 1 ft/lb; accelleration = 40 f/s2; weight of average hand = .86 lb
        self.min_pwr_2h = 0 #1 pwr = 1.5 ft/lb; accelleration = 40 f/s2; weight of average hand = .86 lb

        self.solidness = 1 #Used in damage calc
        self.sharpness = 1 
        self.pointedness = 1

        self.base_attacks = []
        self.attacks = []
        self.base_maneuvers = []
        self.maneuvers = []
        self.guards = []

        self.cost = 0
        self.normality = 1

        for k in kwargs:
            for key in self.__dict__:
                if k == key:
                    self.__dict__.update(kwargs)

        self.set_dynamic_attributes()

    def set_dynamic_attributes(self):
        #Determine which variant name to use
        if len(self.bname_variants) > 1:
            n_idx = roll_dice(1, len(self.bname_variants))
            n_idx -= 1

            self.base_name = self.bname_variants[n_idx]

        #Name weapon using quality and material, if applicable
        if self.main_material is not m_tissue:
            if self.quality != 'Average':
                self.name = self.quality + ' ' + self.main_material.name + ' ' + self.base_name
            else:
                self.name = self.main_material.name + ' ' + self.base_name
        else:
            self.name = self.base_name

        self.attack_mod = 20 * quality_dict.get(self.quality)
        self.parry_mod = 20 * quality_dict.get(self.quality)

        self.main_weight = ((self.main_length * self.avg_main_depth * self.avg_main_width)*self.main_num) * (self.main_material.density * .03)
        self.shaft_weight = (self.shaft_length * self.shaft_diameter * (self.shaft_material.density * .03)) * self.shaft_num
        self.accent_weight = self.accent_cuin * (self.accent_material.density * .03)
        self.grip_weight = self.grip_material.density * (.3 * max(self.hands))
        self.weight = self.main_weight + self.shaft_weight + self.accent_weight
        self.main_only_com = ((self.main_length*self.main_com)+(self.main_loc*self.length))*self.main_weight
        self.shaft_only_com = (self.shaft_length*.5)*self.shaft_weight
        self.accent_only_com = (self.accent_loc*self.length)*self.accent_weight
        self.com = (self.main_only_com + self.shaft_only_com + self.accent_only_com)/self.weight #Center of mass for the whole weapon
        self.com_perc = self.com / self.length #com as a percentage
        self.axis_vs_com = self.com_perc - self.grip_loc #Shows COM relative to grip location (axis for swings). Used to determine AP/stamina costs.

        self.main_hits = (self.main_material.elasticity * 1450000) * (self.main_weight/(self.main_material.density*.03)) * self.main_material.toughness


        self.min_pwr_1h = ((self.added_mass + .86) * 40)/1 #1 pwr = 1 ft/lb/s; accelleration = 40 f/s2; weight of average hand = .86 lb
        self.min_pwr_2h = ((self.added_mass + 1.72) * 40)/1.5 #1 pwr = 1.5 ft/lb/s; accelleration = 40 f/s2; weight of average hand = .86 lb

        if self.main_material.elasticity < 1: self.solidness = self.main_material.elasticity
        if self.main_material.hardness < 1: 
            self.sharpness = self.main_material.hardness
            self.pointedness = self.main_material.hardness
        else:
            self.sharpness = sqrt(self.main_material.hardness)
            self.pointedness = self.main_material.hardness

        #Damage calc = ((((added_mass + fist mass) * velocity) / main_area) * mech_adv) * sharpness or hardness or pointedness

        main_materials_cost = self.main_material.cost * self.main_weight
        shaft_materials_cost = self.shaft_material.cost * self.shaft_weight
        grip_materials_cost = self.grip_material.cost * self.grip_weight
        accent_materials_cost = self.accent_material.cost * self.accent_weight

        #Crafting costs. 1 day skilled labor = ~5 material cost
        main_crafting_cost = self.main_material.craft_diff * self.main_weight
        shaft_crafting_cost = self.shaft_material.craft_diff * self.shaft_weight
        grip_crafting_cost = self.grip_material.craft_diff * self.grip_weight
        accent_crafting_cost = self.accent_material.craft_diff * self.accent_weight


        self.cost = main_crafting_cost + main_materials_cost + shaft_crafting_cost + shaft_materials_cost + grip_crafting_cost + grip_materials_cost + accent_crafting_cost + accent_materials_cost

        self.normality = self.main_material.normality * self.shaft_material.normality * self.grip_material.normality * self.accent_material.normality


class Material():
    _allMaterials = []
    def __init__(self, **kwargs):
        #Below is to create a list of instances of this class
        self._allMaterials.append(self)

        self.name = None
        #Goal: Hardness and elasticity set the hits for the material, toughness then scales them up or down. 
        self.hardness = 1 #Scalar. 1 = ~7 Brinell, roughly very hard wood. Copper 85, Bronze 180, Iron 300, Steel 400-600
        self.elasticity = 1 #Scalar. Basically resistance to deformation. 1 = Wood, average modulus of elasticity ~10 GPa. Bone 18, Gold 79, Copper 100, Bronze 120, Iron 210, Steel 200
        self.toughness = 1 #Scalar. Resistance to breaking when deformed. 1 = Wood, very non-brittle. 10 = Gold, very malliable. .5 = Steel, somewhat brittle. 0.1 = stone
        self.p_ratio = .5 #Poisson's ratio. Non-scaled, use actual values
        self.normality = 1 #Scalar. How rare the material is. 1 = Iron, very common. Gold is 4 million times rarer than Iron, but probably it will be ~.01 in game
        self.cost = 1 #Scalar. Raw cost of the material (taking into account refining costs). 1 = Wood, cheap $0.23/lb. 10 = Copper, $2.50/lb. 82,000 = Pure Gold
        self.craft_diff = 1 #Scalar. Difficulty of crafting. 1 = 1 day of crafting per lb of finished material. Cost of 1 day of a craftsman = 5 
        # For metals, hardness modifies it. Hardened Steel takes 5 days to craft 1 pound of weaponry. 
        self.density = 1 #Scalar. Relative density per cubic inch. 1 = Wood, .03 lb/in3. Copper = .32, Silver = .38, Gold = .7, Iron = .28, Bronze = .3, Steel = .28
        for key in self.__dict__:
            for k in kwargs:
                if k == key:
                    self.__dict__.update(kwargs)
        self.set_dynamic_attr()

    def set_dynamic_attr(self):                   
        if self.craft_diff == 0:
            self.craft_diff = .078 * self.hardness
            self.craft_diff = round(self.craft_diff, 2)

Tags: ofselfcomfortimemainwidthlength
1条回答
网友
1楼 · 发布于 2024-06-01 08:10:52

解决了这个问题;正如用户10987432所怀疑的,它正在创建树上所有对象的副本。我通过移除构造器中的武器引用并将每个属性作为kwarg(ugh)传递来修复它。使一个非常丑陋的4线电话,但它的工作和速度快。一旦它够烦人的话,我就看看能否缩短它;因为现在我只能复制/粘贴,只更改相关字段。你知道吗

Edit:我更清楚地解决了这个问题,将单个攻击设置为攻击类的所有子类,并为每个参与者创建它们的实例。我不知道为什么我最初没有这样做,我已经做了机动,但现在它更容易阅读和调试。再次感谢大家的帮助。你知道吗

相关问题 更多 >