469,592 Members | 2,030 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 469,592 developers. It's quick & easy.

Global vs.Local [solved]

bvdet
2,851 Expert Mod 2GB
I provide shop drawings to structural steel fabricators with SDS/2 software (http://sds2.com) by Design Data (DD). I am not a programmer by education or trade and started writing scripts about 5 years ago. DD has a script interface with Python in the 3D model which is very useful in production. Part of the Python interface includes a dialog box class:
Expand|Select|Wrap|Line Numbers
  1. dlg1 = Dialog('Dialog Box Title')
Method dlg1.done() returns a dictionary. I am exporting the dictionary or list of dictionaries to disc with Pickler and importing with Unpickler to update the variables initialized in the scripts. I have been dumping everything into the global dictionary with globals().update(dd). All variables in the scripts are accessed directly - not through a dictionary. In an effort to evolve towards local variables, I reworked one script this way:
Expand|Select|Wrap|Line Numbers
  1. def run_script():
  2.     from math import sqrt, cos, ..........
  3.     from param import ............
  4.     import macrolib.pickle
  5.     ...............
  6.     # Define default values for variables
  7.     ...............
  8.     # Define functions and classes not imported
  9.     ...............
  10.     # Import defaults file and update local dictionary
  11.     if enable_default_import_export == "Enable":
  12.         dd0 = import_data(default_file_path + def_file)
  13.         if dd0:
  14.             if isinstance(dd0, types.ListType):
  15.                 for dd in dd0:
  16.                     for key, value in dd.items():
  17.                         exec "%s = %s" % (key, repr(value)) in None
  18.             elif isinstance(dd0, types.DictType):
  19.                 for key, value in dd0.items():
  20.                     exec "%s = %s" % (key, repr(value)) in None
  21.             else:
  22.                 Warning("Invalid data - Reverting to original defaults")
  23.     ## Main program loop
  24.     while 1:
  25.         ClearSelection()
  26.         # Select members
  27.  
  28.         # do other stuff as necessary
  29.         ######################################################################################################################################
  30.         ## DIALOG BOX 1 ---------------------------------------------------------------------------------------------------------------------#
  31.         ######################################################################################################################################
  32.         dlg1 = Dialog("Add stiffener plates to beam members")
  33.         dlg1.menu("print_doc", ("Yes", "No"), "No", "Print parametric script documentation only")
  34.         dlg1.tabset_begin()
  35.         dlg1.tab("General Information")
  36.         dlg1.column_group_begin()
  37.         dlg1.column(0)
  38.         if mem2 == "pick point":
  39.             dlg1.group_title("Stiffener Location Selection Method")
  40.             dlg1.menu("location_method", ("Pick Points", "Fixed Spacing"), location_method, "Method of locating stiffeners              ")
  41.             dlg1.entry("points_x_offset", dim_print(points_x_offset), "'Pick Points' member 'X' offset distance")
  42.             dlg1.group_title_end
  43.         dlg1.group_title("Single/Double, Side, Color, Grade")
  44.         dlg1.menu("no_pairs", ("Single", "Double"), no_pairs, "Single or Double Stiffeners               ")
  45.  
  46.         # more dialog box stuff
  47.         try:
  48.             dd1 = dlg1.done()
  49.         except ResponseNotOK:
  50.             break
  51.         for key, value in dd1.items():
  52.             exec "%s = %s" % (key, repr(value)) in None
  53.         ........................................
  54.         # Do what needs to be done
  55.         ........................................
  56.         Add_pls = yes_or_no("Add more full or partial depth stiffener plates to beams?")
  57.         if Add_pls == 0:
  58.             break
  59.         #### END run_script() xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  60. if __name__ == '__main__':
  61.     try:
  62.         run_script()
  63.     finally:
  64.         del run_script
  65.  
This new way seems to work well. It would be a nightmare to rework all my scripts to access values from dictionaries or from dialog box attributes. Is there a potential problem with updating the local dictionary this way or a better way to do it? I will appreciate any suggestions.
Oct 21 '06 #1
7 2166
bartonc
6,596 Expert 4TB
Isn't python a sweet language?! It is THE tool for self-taught people to learn to write well-structured code. Looking out your code, I see a well thought-out implementation that suits your needs (and you say works well). That said, here are a couple of thoughts for you to consider:

1. Python gives access to all kinds of its inner working for us to use.
I have used the exec("a = 0") trick in a large application (gui and all) without any problems. In a simpler module, this is a very cool way to avoid dict[key] referencing.
I ran some tests and found that looping
exec "%s = %s" % (key, repr(value)) in None
is equivalent to
locals().update(dict).
The latter is probably faster, but I didn't do any profiling.

2. By creating a function (and associated name-space) that lasts for the lifetime of the module, you gain two things: eliminating all the built-in stuff that's in globals() when you actually LOOK (print) at the dictionary; it's psycological only. And python may be able to lookup your variable a little quicker due to scope rules (by which, the local name-space is searched first).

3. I may go and rework my stuff after thinking about this!
Thanks for the challenge. It looks to me like you are doing great work.
Have fun!

Barton
Oct 21 '06 #2
bvdet
2,851 Expert Mod 2GB
Thanks for the positive comments! My first choice was locals().update(dict) - of course it does not work because it is read only.
I would rather program than detail steel, but I make my living detailing. It is satisfying to write a script (fun) and use it in producing shop drawings (not so fun). The productivity increase can hit me in the wallet (fun).
Detailing steel in a 3D solids model has a lot of problems that can be solved with the script interface. Example - We can add 'construction circles' in the model by selecting a center point and a radius. A 'construction circle' is a temporary object (it does not become a permanent part of the model) which displays as a circle in the user's current plane. It is useful for determining exact 3D points on the circle. Adding a construction circle by selecting 3 points in 3D space was an interesting problem. We can also add 'construction lines' by selecting 2 points in 3D. Once you get the cons circle, we usually need cons lines laid out radially from the center point and along the arc to determine the exact points for laying out model objects. I developed a class to solve the geometry as follows:
Expand|Select|Wrap|Line Numbers
  1. See my next post for the source code 
  2.  
I am not a geometry expert either, so this took a lot of time. Note the credit to Paul Bourke Geometry - Paul Bourke. Now THAT was fun!
Oct 21 '06 #3
bvdet
2,851 Expert Mod 2GB
Code for previous post:
Expand|Select|Wrap|Line Numbers
  1. """
  2. /// Plane_Intersect3D.py - Given 3 non-collinear points, define three planes and calculate the radius and center point of
  3. /// the circle connecting the points.
  4. /// Determine if a point lies on Plane 1 (defined by initial 3 non-collinear points).
  5. /// Rotate a point about an axis defined by p1 and p1 + N_uv.
  6. /// Developed by Bruce Vaughan, BV Detailing & Design, Inc. (September 2006)
  7. /// URL: www.bvdetailing.com
  8. ####
  9. from math import pi, sqrt, acos, cos, sin
  10. from point import Point
  11. class Plane3D:
  12.     def plane_def(self, p1, p2, p3):
  13.         A = p1.y*(p2.z-p3.z) + p2.y*(p3.z-p1.z) + p3.y*(p1.z-p2.z)
  14.         B = p1.z*(p2.x-p3.x) + p2.z*(p3.x-p1.x) + p3.z*(p1.x-p2.x)
  15.         C = p1.x*(p2.y-p3.y) + p2.x*(p3.y-p1.y) + p3.x*(p1.y-p2.y)
  16.         D = -(p1.x*(p2.y*p3.z-p3.y*p2.z) + p2.x*(p3.y*p1.z-p1.y*p3.z) + p3.x*(p1.y*p2.z-p2.y*p1.z))
  17.         return A, B, C, D
  18.  
  19.     def cross_product(self, p1, p2):
  20.         return Point(p1.y*p2.z - p1.z*p2.y, p1.z*p2.x - p1.x*p2.z, p1.x*p2.y - p1.y*p2.x)
  21.  
  22.     def dot_product(self, p1, p2):
  23.         return (p1.x*p2.x + p1.y*p2.y + p1.z*p2.z)
  24.  
  25.     def __init__(self, p1, p2, p3, theta1 = 0):
  26.         if type(p1) == type(Point(0,0,0)) and type(p2) == type(Point(0,0,0)) and type(p3) == type(Point(0,0,0)):
  27.             """
  28.             /// Paul Bourke - 'Equation of a plane' (March 1989)
  29.             /// Define a plane from 3 non-collinear points
  30.             /// Ax + By + Cz + D = 0
  31.             /// The normal 'N' to the plane is the vector (A, B, C)
  32.             """
  33.             A, B, C, self.D = self.plane_def(p1, p2, p3)
  34.             self.N = Point(A, B, C)
  35.             self.N_len = round(sqrt(A*A + B*B + C*C), 6)
  36.             if self.N_len > 0.0:
  37.                 self.N_uv = Point(self.N.x/self.N_len, self.N.y/self.N_len, self.N.z/self.N_len)
  38.             else:
  39.                 self.N_uv = Point(0.0, 0.0, 0.0)
  40.             # make p1 global to class namespace
  41.             self.p1 = p1
  42.             """
  43.             /// Paul Bourke - 'Equation of a plane' (March 1989)
  44.             /// If vector N is the normal to the plane then all points 'p' on the plane satisfy the following:
  45.             /// N dot p = k where 'dot' is the dot product
  46.             /// N dot p = N.x*p.x + N.y*p.y + N.z*p.z
  47.             """
  48.             self.k = round(self.dot_product(self.N, p1), 6)             # calculation of plane constant 'k'
  49.             self.k0 = round(self.dot_product(self.N_uv, p1), 6)         # displacement of the plane from the origin
  50.             """
  51.             /// Determine vector e and unit vector e0 (p1 to p3)
  52.             /// Determine vector d and unit vector d0 (p1 to p2)
  53.             /// Determine location of point F, midpoint on vector d
  54.             /// Determine location of point G, midpoint on vector e
  55.             """
  56.             e = p3 - p1
  57.             e_len = (sqrt(e.x**2 + e.y**2 + e.z**2))
  58.             if e_len > 0.0:
  59.                 self.e0 = Point(e.x/e_len, e.y/e_len, e.z/e_len)
  60.             else:
  61.                 self.e0 = Point(0.0, 0.0, 0.0)
  62.             d = p2 - p1
  63.             d_len = (sqrt(d.x**2 + d.y**2 + d.z**2))
  64.             if d_len > 0.0:
  65.                 self.d0 = Point(d.x/d_len, d.y/d_len, d.z/d_len)
  66.             else:
  67.                 self.d0 = Point(0.0, 0.0, 0.0) 
  68.             self.F = Point(p1.x + (d.x/2), p1.y + (d.y/2), p1.z + (d.z/2))
  69.             self.G = Point(p1.x + (e.x/2), p1.y + (e.y/2), p1.z + (e.z/2))
  70.             # Make variables 'e' and 'd' available as attributes
  71.             self.e = e
  72.             self.d = d
  73.  
  74.             # Calculate distance between points p1 and p2
  75.             self.Ra = p2.dist(p1)
  76.  
  77.             """
  78.             /// Calculate net angle between vectors d0 and e0 (Q)
  79.             /// Radius = self.Ra
  80.             /// Calculate point to point distance (pp)
  81.             """
  82.             if abs(theta1) == pi:
  83.                 self.Q = theta1
  84.             else:
  85.                 self.Q = acos(self.dot_product(self.d0, self.e0))   # radians
  86.             self.pp = abs(self.Q * self.Ra)            
  87.  
  88.         else:
  89.             raise TypeError, 'The arguments passed to Plane3D must be a POINT'
  90.  
  91.     # Calculate points to define plane #2 and calculate N2 and c2
  92.     def plane_2(self):
  93.         p1 = self.G
  94.         p2 = self.G + self.N_uv
  95.         p3 = self.G + self.cross_product(self.e0, self.N_uv)
  96.         A, B, C, D = self.plane_def(p1, p2, p3)
  97.         d = round(sqrt(A*A + B*B + C*C), 6) 
  98.         self.N2 = Point(A/d, B/d, C/d)
  99.         self.c2 = round(self.N2.x*p1.x + self.N2.y*p1.y + self.N2.z*p1.z, 6)
  100.  
  101.     # Calculate points to define plane #3 and calculate N3 and c3
  102.     def plane_3(self):
  103.         p1 = self.F
  104.         p2 = self.F + self.N_uv
  105.         p3 = self.F + self.cross_product(self.d0, self.N_uv)
  106.         A, B, C, D = self.plane_def(p1, p2, p3)
  107.         d = round(sqrt(A*A + B*B + C*C), 6)
  108.         self.N3 = Point(A/d, B/d, C/d)
  109.         self.c3 = round(self.N3.x*p1.x + self.N3.y*p1.y + self.N3.z*p1.z, 6)
  110.  
  111.     def three_pt_circle (self):     
  112.         """
  113.         /// Paul Bourke - 'Intersection of three planes' (October 2001)
  114.         /// The intersection of three planes is either a point, a line, or there is no intersection.
  115.         /// Three planes can be written as:
  116.         /// N1 dot p = c1
  117.         /// N2 dot p = c2
  118.         /// N3 dot p = c3
  119.         /// 'Nx' is the normal vector
  120.         /// 'p' is a point on the plane
  121.         /// 'dot' signifies the dot product of 'Nx' and 'p'
  122.         /// 'cx' = plane constant (displacement of the plane from the origin if Nx is a unit vector)
  123.         /// The intersection point of the three planes "M" is given by:
  124.         /// M = (c1*(N2 cross N3) + c2(N3 cross N1) + c3*(N1 cross N2)) / (N1 dot (N2 cross N3))
  125.         /// 'cross' indicates the cross product and 'dot' indicates the dot product
  126.         /// Calculate the center point of the circle (intersection point of three planes) and the radius.
  127.         /// Plane 1 is defined by the three points initially passed to class object.
  128.         """
  129.         # Define Plane 2
  130.         self.plane_2()
  131.         # Define Plane 3
  132.         self.plane_3()
  133.  
  134.         N23 = self.cross_product(self.N2, self.N3)
  135.         N31 = self.cross_product(self.N3, self.N)
  136.         N12 = self.cross_product(self.N, self.N2)
  137.         NdN23 = round(self.dot_product(self.N, N23), 6)
  138.  
  139.         numer = Point(self.k*N23.x, self.k*N23.y, self.k*N23.z) + (self.c2*N31.x, self.c2*N31.y, self.c2*N31.z) + \
  140.                      (self.c3*N12.x, self.c3*N12.y, self.c3*N12.z)
  141.         if NdN23 != 0.0:
  142.             self.M = Point(numer.x/NdN23, numer.y/NdN23, numer.z/NdN23)
  143.             self.R = self.M.dist(self.p1)
  144.         else:
  145.             self.M = Point(0.0, 0.0, 0.0)
  146.             self.R = 0.0
  147.  
  148.     """
  149.     /// Credit Paul Bourke for 'Rotate A Point About An Arbitrary Axis (3D)' (August 2002)
  150.     /// Rotate point p about a line passing through self.p1 and normal to the current plane by the angle 'theta' in radians
  151.     /// Return the new point
  152.     """    
  153.     def PointRotate3D(self, p, theta):
  154.         q2 = Point(0.0,0.0,0.0)
  155.  
  156.         # step 1 - translate space so that the rotation axis passes through the origin
  157.         q1 = p - self.p1
  158.         u = self.N_uv
  159.         d = sqrt(u.y*u.y + u.z*u.z)
  160.  
  161.         # step 2 - rotate space about the x axis so that the rotation axis lies in the xz plane
  162.         if d != 0.0:
  163.             q2.x = q1.x
  164.             q2.y = q1.y*u.z/d - q1.z*u.y/d
  165.             q2.z = q1.y*u.y/d + q1.z*u.z/d
  166.         else:
  167.             q2 = q1
  168.  
  169.         # step 3 - rotate space about the y axis so that the rotation axis lies along the positive z axis
  170.         q1.x = q2.x*d - q2.z*u.x
  171.         q1.y = q2.y
  172.         q1.z = q2.x*u.x + q2.z*d
  173.  
  174.         # step 4 - rotate about the z axis
  175.         q2.x = q1.x*cos(theta) - q1.y*sin(theta)
  176.         q2.y = q1.x*sin(theta) + q1.y*cos(theta)
  177.         q2.z = q1.z
  178.  
  179.         # inverse of step 3
  180.         q1.x = q2.x*d + q2.z*u.x
  181.         q1.y = q2.y
  182.         q1.z = -q2.x*u.x + q2.z*d
  183.  
  184.         # inverse of step 2
  185.         if d != 0.0:
  186.             q2.x = q1.x
  187.             q2.y = q1.y*u.z/d + q1.z*u.y/d
  188.             q2.z = -q1.y*u.y/d + q1.z*u.z/d
  189.         else:
  190.             q2 = q1  
  191.  
  192.         # inverse of step 1        
  193.         q1 = q2 + self.p1        
  194.  
  195.         # return rotated point
  196.         return q1            
  197.  
  198.     def lie_check(self, p4):
  199.         """
  200.         /// Paul Bourke - 'Equation of a plane' (March 1989)
  201.         /// Given any point 'a' on a plane: N dot (a-p) = 0
  202.         """
  203.         return round((self.N.x*(p4.x-self.p1.x) + self.N.y*(p4.y-self.p1.y) + self.N.z*(p4.z-self.p1.z)), 4)
  204.  
  205.     def lie_check2(self, p5):
  206.         """
  207.         /// Paul Bourke - 'Equation of a plane' (March 1989)
  208.         /// s = A*p5.x + B*p5.y + C*p5.z + D 
  209.         /// If s > 0, point p5 lies on the same side as self.N
  210.         /// If s < 0, point p5 lies on the oppsite side from self.N
  211.         /// If s = 0, point p5 lies on the plane
  212.         """
  213.         return round((self.N.x*p5.x + self.N.y*p5.y + self.N.z*p5.z + self.D), 4)
Oct 21 '06 #4
bartonc
6,596 Expert 4TB
Thanks for the positive comments! My first choice was locals().update(dict) - of course it does not work because it is read only.
I don't understand the "read only" thing. In version 2.4 a module with the following code works. What version of python are you running?
Expand|Select|Wrap|Line Numbers
  1. def test():
  2.     print "in test()"
  3.     locals().update({"a":0})
  4.     print locals()
  5.     print globals()
  6. test()
  7. print "module name-space"
  8. print locals()
  9. print globals()
  10.  
Oct 21 '06 #5
bvdet
2,851 Expert Mod 2GB
I don't understand the "read only" thing. In version 2.4 a module with the following code works. What version of python are you running?
Expand|Select|Wrap|Line Numbers
  1. def test():
  2.     print "in test()"
  3.     locals().update({"a":0})
  4.     print locals()
  5.     print globals()
  6. test()
  7. print "module name-space"
  8. print locals()
  9. print globals()
  10.  
Version 2.3 is installed with each installation of SDS/2. Believe me, I tried and tried but locals().update(dd) would not work for my application. Try this:
Expand|Select|Wrap|Line Numbers
  1. def test():
  2.         a = 10
  3.     print "in test()"
  4.     locals().update({"a":0})
  5.     print locals()
  6.     print globals()
  7. test()
  8.  
Does 'a' update to 10?
Oct 21 '06 #6
bvdet
2,851 Expert Mod 2GB
Sorry, I meant update from 10 to 0.
Oct 22 '06 #7
bartonc
6,596 Expert 4TB
Sorry, I meant update from 10 to 0.
Nope, it doesn't update. I did find this, which I didn't expect.

locals()Update and return a dictionary representing the current local symbol table. Warning: The contents of this dictionary should not be modified; changes may not affect the values of local variables used by the interpreter.

So exec() IS the legal way of changing this dict.
Oct 22 '06 #8

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

8 posts views Thread by David Hitillambeau | last post: by
12 posts views Thread by John M | last post: by
7 posts views Thread by Ankit Aneja | last post: by
10 posts views Thread by m.epper | last post: by
9 posts views Thread by Ed Jensen | last post: by
2 posts views Thread by Florian Lindner | last post: by
112 posts views Thread by istillshine | last post: by
4 posts views Thread by raylopez99 | last post: by
reply views Thread by suresh191 | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.